lit-html
Advanced tools
Comparing version 0.11.2 to 0.11.3
@@ -14,3 +14,15 @@ # Change Log | ||
<!-- ## Unreleased --> | ||
<!-- ### Added --> | ||
<!-- ### Changed --> | ||
<!-- ### Removed --> | ||
<!-- ### Fixed --> | ||
## [0.11.3] - 2018-09-13 | ||
### Changed | ||
* Moved upgrading of custom elements in template fragments to a common location in TemplateInstance ([#489](https://github.com/Polymer/lit-html/pull/489)) | ||
* Rewrite render() to reuse the logic in NodePart. render() now supports all the data types that NodeParts do. ([#491](https://github.com/Polymer/lit-html/pull/491)) | ||
### Fixed | ||
* Fixed bug when using the ShadyCSS @apply` shim. ([#502](https://github.com/Polymer/lit-html/pull/502)) | ||
## [0.11.2] - 2018-09-12 | ||
@@ -21,4 +33,2 @@ | ||
<!-- ### Changed --> | ||
<!-- ### Removed --> | ||
### Fixed | ||
@@ -25,0 +35,0 @@ |
@@ -21,3 +21,3 @@ /** | ||
}; | ||
import { directive, NodePart } from '../lit-html.js'; | ||
import { createMarker, directive, NodePart } from '../lit-html.js'; | ||
/** | ||
@@ -82,3 +82,3 @@ * A directive that renders the items of an async iterable[1], appending new | ||
// Create a new node to separate the previous and next Parts | ||
itemStartNode = document.createComment(''); | ||
itemStartNode = createMarker(); | ||
// itemPart is currently the Part for the previous item. Set | ||
@@ -85,0 +85,0 @@ // it's endNode to the node we'll use for the next Part's |
@@ -14,3 +14,3 @@ /** | ||
*/ | ||
import { Directive, AttributePart } from '../lit-html.js'; | ||
import { AttributePart, Directive } from '../lit-html.js'; | ||
export interface ClassInfo { | ||
@@ -20,7 +20,8 @@ [name: string]: string | boolean | number; | ||
/** | ||
* A directive that applies CSS classes. This must be used in the `class` attribute | ||
* and must be the only part used in the attribute. It takes each property in the | ||
* `classInfo` argument and adds the property name to the element's `classList` | ||
* if the property value is truthy; if the property value is falsey, the | ||
* property name is removed from the element's `classList`. For example | ||
* A directive that applies CSS classes. This must be used in the `class` | ||
* attribute and must be the only part used in the attribute. It takes each | ||
* property in the `classInfo` argument and adds the property name to the | ||
* element's `classList` if the property value is truthy; if the property value | ||
* is falsey, the property name is removed from the element's `classList`. For | ||
* example | ||
* `{foo: bar}` applies the class `foo` if the value of `bar` is truthy. | ||
@@ -27,0 +28,0 @@ * @param classInfo {ClassInfo} |
@@ -14,3 +14,3 @@ /** | ||
*/ | ||
import { directive, AttributePart, PropertyPart } from '../lit-html.js'; | ||
import { AttributePart, directive, PropertyPart } from '../lit-html.js'; | ||
// On IE11, classList.toggle doesn't accept a second argument. | ||
@@ -42,7 +42,8 @@ // Since this is so minor, we just polyfill it. | ||
/** | ||
* A directive that applies CSS classes. This must be used in the `class` attribute | ||
* and must be the only part used in the attribute. It takes each property in the | ||
* `classInfo` argument and adds the property name to the element's `classList` | ||
* if the property value is truthy; if the property value is falsey, the | ||
* property name is removed from the element's `classList`. For example | ||
* A directive that applies CSS classes. This must be used in the `class` | ||
* attribute and must be the only part used in the attribute. It takes each | ||
* property in the `classInfo` argument and adds the property name to the | ||
* element's `classList` if the property value is truthy; if the property value | ||
* is falsey, the property name is removed from the element's `classList`. For | ||
* example | ||
* `{foo: bar}` applies the class `foo` if the value of `bar` is truthy. | ||
@@ -49,0 +50,0 @@ * @param classInfo {ClassInfo} |
@@ -14,3 +14,3 @@ /** | ||
*/ | ||
import { directive, NodePart, removeNodes, reparentNodes } from '../lit-html.js'; | ||
import { createMarker, directive, NodePart, removeNodes, reparentNodes } from '../lit-html.js'; | ||
const keyMapCache = new WeakMap(); | ||
@@ -61,4 +61,4 @@ function cleanMap(part, key, map) { | ||
// now and revisit soon. | ||
const marker = document.createComment(''); | ||
const endNode = document.createComment(''); | ||
const marker = createMarker(); | ||
const endNode = createMarker(); | ||
container.insertBefore(marker, currentMarker); | ||
@@ -65,0 +65,0 @@ container.insertBefore(endNode, currentMarker); |
@@ -14,3 +14,3 @@ /** | ||
*/ | ||
import { Directive, AttributePart } from '../lit-html.js'; | ||
import { AttributePart, Directive } from '../lit-html.js'; | ||
export interface StyleInfo { | ||
@@ -20,9 +20,9 @@ [name: string]: string; | ||
/** | ||
* A directive that applies CSS properties. This must be used in the `style` attribute | ||
* and must be the only part used in the attribute. It takes the property names in | ||
* the `styleInfo` object and adds the property values as a css style propertes. | ||
* For example `{backgroundColor: 'red', borderTop: '5px'}` sets these properties | ||
* to the element's style. | ||
* A directive that applies CSS properties. This must be used in the `style` | ||
* attribute and must be the only part used in the attribute. It takes the | ||
* property names in the `styleInfo` object and adds the property values as a | ||
* css style propertes. For example `{backgroundColor: 'red', borderTop: '5px'}` | ||
* sets these properties to the element's style. | ||
* @param styleInfo {StyleInfo} | ||
*/ | ||
export declare const styleMap: (styleInfo: StyleInfo) => Directive<AttributePart>; |
@@ -14,3 +14,3 @@ /** | ||
*/ | ||
import { directive, AttributePart, PropertyPart } from '../lit-html.js'; | ||
import { AttributePart, directive, PropertyPart } from '../lit-html.js'; | ||
/** | ||
@@ -22,5 +22,5 @@ * Stores the StyleInfo object applied to a given AttributePart. | ||
/** | ||
* Stores AttributeParts that have had static styles applied (e.g. `height: 0;` in | ||
* style="height: 0; ${styleMap()}"). Static styles are applied only the first time | ||
* the directive is run on a part. | ||
* Stores AttributeParts that have had static styles applied (e.g. `height: 0;` | ||
* in style="height: 0; ${styleMap()}"). Static styles are applied only the | ||
* first time the directive is run on a part. | ||
*/ | ||
@@ -30,7 +30,7 @@ // Note, could be a WeakSet, but prefer not requiring this polyfill. | ||
/** | ||
* A directive that applies CSS properties. This must be used in the `style` attribute | ||
* and must be the only part used in the attribute. It takes the property names in | ||
* the `styleInfo` object and adds the property values as a css style propertes. | ||
* For example `{backgroundColor: 'red', borderTop: '5px'}` sets these properties | ||
* to the element's style. | ||
* A directive that applies CSS properties. This must be used in the `style` | ||
* attribute and must be the only part used in the attribute. It takes the | ||
* property names in the `styleInfo` object and adds the property values as a | ||
* css style propertes. For example `{backgroundColor: 'red', borderTop: '5px'}` | ||
* sets these properties to the element's style. | ||
* @param styleInfo {StyleInfo} | ||
@@ -37,0 +37,0 @@ */ |
@@ -50,2 +50,8 @@ /** | ||
/** | ||
* Inserts this part into a container. | ||
* | ||
* This part must be empty, as its contents are not automatically moved. | ||
*/ | ||
appendInto(container: Node): void; | ||
/** | ||
* Inserts this part between `ref` and `ref`'s next sibling. Both `ref` and | ||
@@ -52,0 +58,0 @@ * its next sibling must be static, unchanging nodes such as those that appear |
@@ -15,3 +15,3 @@ /** | ||
import { isDirective } from './directive.js'; | ||
import { isCEPolyfill, removeNodes } from './dom.js'; | ||
import { removeNodes } from './dom.js'; | ||
import { noChange } from './part.js'; | ||
@@ -109,2 +109,11 @@ import { TemplateInstance } from './template-instance.js'; | ||
/** | ||
* Inserts this part into a container. | ||
* | ||
* This part must be empty, as its contents are not automatically moved. | ||
*/ | ||
appendInto(container) { | ||
this.startNode = container.appendChild(createMarker()); | ||
this.endNode = container.appendChild(createMarker()); | ||
} | ||
/** | ||
* Inserts this part between `ref` and `ref`'s next sibling. Both `ref` and | ||
@@ -211,7 +220,2 @@ * its next sibling must be static, unchanging nodes such as those that appear | ||
const fragment = instance._clone(); | ||
// Since we cloned in the polyfill case, now force an upgrade | ||
if (isCEPolyfill) { | ||
document.adoptNode(fragment); | ||
customElements.upgrade(fragment); | ||
} | ||
instance.update(value.values); | ||
@@ -218,0 +222,0 @@ this._commitNode(fragment); |
@@ -14,6 +14,6 @@ /** | ||
*/ | ||
import { NodePart } from './parts.js'; | ||
import { TemplateFactory } from './template-factory.js'; | ||
import { TemplateInstance } from './template-instance.js'; | ||
import { TemplateResult } from './template-result.js'; | ||
export declare const templateInstances: WeakMap<Node, TemplateInstance>; | ||
export declare const parts: WeakMap<Node, NodePart>; | ||
/** | ||
@@ -20,0 +20,0 @@ * Renders a template to a container. |
@@ -14,6 +14,6 @@ /** | ||
*/ | ||
import { isCEPolyfill, removeNodes } from './dom.js'; | ||
import { removeNodes } from './dom.js'; | ||
import { NodePart } from './parts.js'; | ||
import { templateFactory as defaultTemplateFactory } from './template-factory.js'; | ||
import { TemplateInstance } from './template-instance.js'; | ||
export const templateInstances = new WeakMap(); | ||
export const parts = new WeakMap(); | ||
/** | ||
@@ -34,23 +34,11 @@ * Renders a template to a container. | ||
export function render(result, container, templateFactory = defaultTemplateFactory) { | ||
const template = templateFactory(result); | ||
let instance = templateInstances.get(container); | ||
// Repeat render, just call update() | ||
if (instance !== undefined && instance.template === template && | ||
instance.processor === result.processor) { | ||
instance.update(result.values); | ||
return; | ||
let part = parts.get(container); | ||
if (part === undefined) { | ||
removeNodes(container, container.firstChild); | ||
parts.set(container, part = new NodePart(templateFactory)); | ||
part.appendInto(container); | ||
} | ||
// First render, create a new TemplateInstance and append it | ||
instance = new TemplateInstance(template, result.processor, templateFactory); | ||
templateInstances.set(container, instance); | ||
const fragment = instance._clone(); | ||
removeNodes(container, container.firstChild); | ||
// Since we cloned in the polyfill case, now force an upgrade | ||
if (isCEPolyfill && !container.isConnected) { | ||
document.adoptNode(fragment); | ||
customElements.upgrade(fragment); | ||
} | ||
container.appendChild(fragment); | ||
instance.update(result.values); | ||
part.setValue(result); | ||
part.commit(); | ||
} | ||
//# sourceMappingURL=render.js.map |
@@ -14,7 +14,6 @@ /** | ||
*/ | ||
import { removeNodes } from './dom.js'; | ||
import { insertNodeIntoTemplate, removeNodesFromTemplate } from './modify-template.js'; | ||
import { templateInstances } from './render.js'; | ||
import { parts, render as litRender } from './render.js'; | ||
import { templateCaches } from './template-factory.js'; | ||
import { TemplateInstance } from './template-instance.js'; | ||
import { TemplateResult } from './template-result.js'; | ||
import { Template } from './template.js'; | ||
@@ -91,55 +90,45 @@ export { html, svg, TemplateResult } from '../lit-html.js'; | ||
*/ | ||
const ensureStylesScoped = (fragment, template, scopeName) => { | ||
// only scope element template once per scope name | ||
if (!shadyRenderSet.has(scopeName)) { | ||
shadyRenderSet.add(scopeName); | ||
const styleTemplate = document.createElement('template'); | ||
Array.from(fragment.querySelectorAll('style')).forEach((s) => { | ||
styleTemplate.content.appendChild(s); | ||
}); | ||
window.ShadyCSS.prepareTemplateStyles(styleTemplate, scopeName); | ||
// Fix templates: note the expectation here is that the given `fragment` | ||
// has been generated from the given `template` which contains | ||
// the set of templates rendered into this scope. | ||
// It is only from this set of initial templates from which styles | ||
// will be scoped and removed. | ||
removeStylesFromLitTemplates(scopeName); | ||
// ApplyShim case | ||
if (window.ShadyCSS.nativeShadow) { | ||
const style = styleTemplate.content.querySelector('style'); | ||
if (style !== null) { | ||
// Insert style into rendered fragment | ||
fragment.insertBefore(style, fragment.firstChild); | ||
// Insert into lit-template (for subsequent renders) | ||
insertNodeIntoTemplate(template, style.cloneNode(true), template.element.content.firstChild); | ||
} | ||
const styleTemplatesForScope = (fragment, template, scopeName) => { | ||
shadyRenderSet.add(scopeName); | ||
// Move styles out of rendered DOM and store. | ||
const styles = fragment.querySelectorAll('style'); | ||
const styleFragment = document.createDocumentFragment(); | ||
for (let i = 0; i < styles.length; i++) { | ||
styleFragment.appendChild(styles[i]); | ||
} | ||
// Remove styles from nested templates in this scope. | ||
removeStylesFromLitTemplates(scopeName); | ||
// And then put them into the "root" template passed in as `template`. | ||
insertNodeIntoTemplate(template, styleFragment, template.element.content.firstChild); | ||
// Note, it's important that ShadyCSS gets the template that `lit-html` | ||
// will actually render so that it can update the style inside when | ||
// needed. | ||
window.ShadyCSS.prepareTemplateStyles(template.element, scopeName); | ||
// When using native Shadow DOM, replace the style in the rendered | ||
// fragment. | ||
if (window.ShadyCSS.nativeShadow) { | ||
const style = template.element.content.querySelector('style'); | ||
if (style !== null) { | ||
fragment.insertBefore(style.cloneNode(true), fragment.firstChild); | ||
} | ||
} | ||
}; | ||
// NOTE: We're copying code from lit-html's `render` method here. | ||
// We're doing this explicitly because the API for rendering templates is likely | ||
// to change in the near term. | ||
export function render(result, container, scopeName) { | ||
const templateFactory = shadyTemplateFactory(scopeName); | ||
const template = templateFactory(result); | ||
let instance = templateInstances.get(container); | ||
// Repeat render, just call update() | ||
if (instance !== undefined && instance.template === template && | ||
instance.processor === result.processor) { | ||
instance.update(result.values); | ||
return; | ||
} | ||
// First render, create a new TemplateInstance and append it | ||
instance = new TemplateInstance(template, result.processor, templateFactory); | ||
templateInstances.set(container, instance); | ||
const fragment = instance._clone(); | ||
instance.update(result.values); | ||
// If there's a shadow host, do ShadyCSS scoping... | ||
if (container instanceof ShadowRoot && compatibleShadyCSSVersion) { | ||
ensureStylesScoped(fragment, template, scopeName); | ||
const shouldScope = container instanceof ShadowRoot && compatibleShadyCSSVersion; | ||
const hasScoped = shadyRenderSet.has(scopeName); | ||
// Call `styleElement` to update element if it's already been processed. | ||
// This ensures the template is up to date before stamping the template | ||
// into the shadowRoot *or* updates the shadowRoot if we've already stamped | ||
// and are just updating the template. | ||
if (shouldScope && hasScoped) { | ||
window.ShadyCSS.styleElement(container.host); | ||
} | ||
removeNodes(container, container.firstChild); | ||
container.appendChild(fragment); | ||
litRender(result, container, shadyTemplateFactory(scopeName)); | ||
// When rendering a TemplateResult, scope the template with ShadyCSS | ||
if (shouldScope && !hasScoped && result instanceof TemplateResult) { | ||
const part = parts.get(container); | ||
const instance = part.value; | ||
styleTemplatesForScope(container, instance.template, scopeName); | ||
} | ||
} | ||
//# sourceMappingURL=shady-render.js.map |
@@ -92,2 +92,6 @@ /** | ||
_prepareInstance(fragment); | ||
if (isCEPolyfill) { | ||
document.adoptNode(fragment); | ||
customElements.upgrade(fragment); | ||
} | ||
return fragment; | ||
@@ -94,0 +98,0 @@ } |
{ | ||
"name": "lit-html", | ||
"version": "0.11.2", | ||
"version": "0.11.3", | ||
"description": "HTML template literals in JavaScript", | ||
@@ -23,7 +23,7 @@ "license": "BSD-3-Clause", | ||
"build": "tsc", | ||
"checksize": "rollup -c ; rm lit-html.bundled.js", | ||
"checksize": "rollup -c ; cat lit-html.bundled.js | gzip -9 | wc -c ; rm lit-html.bundled.js", | ||
"gen-docs": "typedoc --readme none --mode modules --excludeNotExported --excludePrivate --exclude **/*_test.ts --out ./docs/api ./src", | ||
"test": "npm run build && npm run lint && wct --npm", | ||
"quicktest": "wct -l chrome -p --npm", | ||
"format": "find src test | grep '\\.js$\\|\\.ts$' | xargs clang-format --style=file -i", | ||
"format": "clang-format --version; find src test | grep '\\.js$\\|\\.ts$' | xargs clang-format --style=file -i", | ||
"lint": "tslint --project ./", | ||
@@ -38,2 +38,3 @@ "prepare": "npm run build" | ||
"chai": "^4.1.2", | ||
"clang-format": "^1.2.4", | ||
"mocha": "^5.2.0", | ||
@@ -44,3 +45,3 @@ "rollup": "^0.64.1", | ||
"tslint": "^5.9.1", | ||
"typedoc": "^0.11.1", | ||
"typedoc": "^0.12.0", | ||
"typescript": "^2.8.3", | ||
@@ -47,0 +48,0 @@ "uglify-es": "^3.3.5", |
@@ -15,3 +15,3 @@ /** | ||
import {directive, Directive, NodePart} from '../lit-html.js'; | ||
import {createMarker, directive, Directive, NodePart} from '../lit-html.js'; | ||
@@ -83,3 +83,3 @@ /** | ||
// Create a new node to separate the previous and next Parts | ||
itemStartNode = document.createComment(''); | ||
itemStartNode = createMarker(); | ||
// itemPart is currently the Part for the previous item. Set | ||
@@ -86,0 +86,0 @@ // it's endNode to the node we'll use for the next Part's |
@@ -15,3 +15,3 @@ /** | ||
import {directive, Directive, AttributePart, PropertyPart} from '../lit-html.js'; | ||
import {AttributePart, directive, Directive, PropertyPart} from '../lit-html.js'; | ||
@@ -22,3 +22,4 @@ | ||
if (window.navigator.userAgent.match('Trident')) { | ||
DOMTokenList.prototype.toggle = function(token: string, force?: boolean|undefined) { | ||
DOMTokenList.prototype.toggle = function( | ||
token: string, force?: boolean|undefined) { | ||
if (force === undefined || force) { | ||
@@ -52,7 +53,8 @@ this.add(token); | ||
/** | ||
* A directive that applies CSS classes. This must be used in the `class` attribute | ||
* and must be the only part used in the attribute. It takes each property in the | ||
* `classInfo` argument and adds the property name to the element's `classList` | ||
* if the property value is truthy; if the property value is falsey, the | ||
* property name is removed from the element's `classList`. For example | ||
* A directive that applies CSS classes. This must be used in the `class` | ||
* attribute and must be the only part used in the attribute. It takes each | ||
* property in the `classInfo` argument and adds the property name to the | ||
* element's `classList` if the property value is truthy; if the property value | ||
* is falsey, the property name is removed from the element's `classList`. For | ||
* example | ||
* `{foo: bar}` applies the class `foo` if the value of `bar` is truthy. | ||
@@ -62,29 +64,31 @@ * @param classInfo {ClassInfo} | ||
export const classMap = (classInfo: ClassInfo): Directive<AttributePart> => | ||
directive((part: AttributePart) => { | ||
if (!(part instanceof AttributePart) || (part instanceof PropertyPart) || | ||
part.committer.name !== 'class' || part.committer.parts.length > 1) { | ||
throw new Error('The `classMap` directive must be used in the `class` attribute ' + | ||
'and must be the only part in the attribute.'); | ||
} | ||
// handle static classes | ||
if (!classMapStatics.has(part)) { | ||
part.committer.element.className = part.committer.strings.join(' '); | ||
classMapStatics.set(part, true); | ||
} | ||
// remove old classes that no longer apply | ||
const oldInfo = classMapCache.get(part); | ||
for (const name in oldInfo) { | ||
if (!(name in classInfo)) { | ||
part.committer.element.classList.remove(name); | ||
directive((part: AttributePart) => { | ||
if (!(part instanceof AttributePart) || (part instanceof PropertyPart) || | ||
part.committer.name !== 'class' || part.committer.parts.length > 1) { | ||
throw new Error( | ||
'The `classMap` directive must be used in the `class` attribute ' + | ||
'and must be the only part in the attribute.'); | ||
} | ||
} | ||
// add new classes | ||
for (const name in classInfo) { | ||
if (!oldInfo || (oldInfo[name] !== classInfo[name])) { | ||
// We explicitly want a loose truthy check here because | ||
// it seems more convenient that '' and 0 are skipped. | ||
part.committer.element.classList.toggle(name, Boolean(classInfo[name])); | ||
// handle static classes | ||
if (!classMapStatics.has(part)) { | ||
part.committer.element.className = part.committer.strings.join(' '); | ||
classMapStatics.set(part, true); | ||
} | ||
} | ||
classMapCache.set(part, classInfo); | ||
}); | ||
// remove old classes that no longer apply | ||
const oldInfo = classMapCache.get(part); | ||
for (const name in oldInfo) { | ||
if (!(name in classInfo)) { | ||
part.committer.element.classList.remove(name); | ||
} | ||
} | ||
// add new classes | ||
for (const name in classInfo) { | ||
if (!oldInfo || (oldInfo[name] !== classInfo[name])) { | ||
// We explicitly want a loose truthy check here because | ||
// it seems more convenient that '' and 0 are skipped. | ||
part.committer.element.classList.toggle( | ||
name, Boolean(classInfo[name])); | ||
} | ||
} | ||
classMapCache.set(part, classInfo); | ||
}); |
@@ -15,3 +15,3 @@ /** | ||
import {directive, Directive, NodePart, removeNodes, reparentNodes} from '../lit-html.js'; | ||
import {createMarker, directive, Directive, NodePart, removeNodes, reparentNodes} from '../lit-html.js'; | ||
@@ -78,4 +78,4 @@ export type KeyFn<T> = (item: T) => any; | ||
// now and revisit soon. | ||
const marker = document.createComment(''); | ||
const endNode = document.createComment(''); | ||
const marker = createMarker(); | ||
const endNode = createMarker(); | ||
container.insertBefore(marker, currentMarker); | ||
@@ -82,0 +82,0 @@ container.insertBefore(endNode, currentMarker); |
@@ -15,3 +15,3 @@ /** | ||
import {directive, Directive, AttributePart, PropertyPart} from '../lit-html.js'; | ||
import {AttributePart, directive, Directive, PropertyPart} from '../lit-html.js'; | ||
@@ -29,5 +29,5 @@ export interface StyleInfo { | ||
/** | ||
* Stores AttributeParts that have had static styles applied (e.g. `height: 0;` in | ||
* style="height: 0; ${styleMap()}"). Static styles are applied only the first time | ||
* the directive is run on a part. | ||
* Stores AttributeParts that have had static styles applied (e.g. `height: 0;` | ||
* in style="height: 0; ${styleMap()}"). Static styles are applied only the | ||
* first time the directive is run on a part. | ||
*/ | ||
@@ -38,31 +38,32 @@ // Note, could be a WeakSet, but prefer not requiring this polyfill. | ||
/** | ||
* A directive that applies CSS properties. This must be used in the `style` attribute | ||
* and must be the only part used in the attribute. It takes the property names in | ||
* the `styleInfo` object and adds the property values as a css style propertes. | ||
* For example `{backgroundColor: 'red', borderTop: '5px'}` sets these properties | ||
* to the element's style. | ||
* A directive that applies CSS properties. This must be used in the `style` | ||
* attribute and must be the only part used in the attribute. It takes the | ||
* property names in the `styleInfo` object and adds the property values as a | ||
* css style propertes. For example `{backgroundColor: 'red', borderTop: '5px'}` | ||
* sets these properties to the element's style. | ||
* @param styleInfo {StyleInfo} | ||
*/ | ||
export const styleMap = (styleInfo: StyleInfo): Directive<AttributePart> => | ||
directive((part: AttributePart) => { | ||
if (!(part instanceof AttributePart) || (part instanceof PropertyPart) || | ||
part.committer.name !== 'style' || part.committer.parts.length > 1) { | ||
throw new Error('The `styleMap` directive must be used in the style attribute ' + | ||
'and must be the only part in the attribute.'); | ||
} | ||
// handle static styles | ||
if (!styleMapStatics.has(part)) { | ||
(part.committer.element as HTMLElement).style.cssText = | ||
part.committer.strings.join(' '); | ||
styleMapStatics.set(part, true); | ||
} | ||
// remove old styles that no longer apply | ||
const oldInfo = styleMapCache.get(part); | ||
for (const name in oldInfo) { | ||
if (!(name in styleInfo)) { | ||
((part.committer.element as HTMLElement).style as any)[name] = null; | ||
directive((part: AttributePart) => { | ||
if (!(part instanceof AttributePart) || (part instanceof PropertyPart) || | ||
part.committer.name !== 'style' || part.committer.parts.length > 1) { | ||
throw new Error( | ||
'The `styleMap` directive must be used in the style attribute ' + | ||
'and must be the only part in the attribute.'); | ||
} | ||
} | ||
Object.assign((part.committer.element as HTMLElement).style, styleInfo); | ||
styleMapCache.set(part, styleInfo); | ||
}); | ||
// handle static styles | ||
if (!styleMapStatics.has(part)) { | ||
(part.committer.element as HTMLElement).style.cssText = | ||
part.committer.strings.join(' '); | ||
styleMapStatics.set(part, true); | ||
} | ||
// remove old styles that no longer apply | ||
const oldInfo = styleMapCache.get(part); | ||
for (const name in oldInfo) { | ||
if (!(name in styleInfo)) { | ||
((part.committer.element as HTMLElement).style as any)[name] = null; | ||
} | ||
} | ||
Object.assign((part.committer.element as HTMLElement).style, styleInfo); | ||
styleMapCache.set(part, styleInfo); | ||
}); |
@@ -16,3 +16,3 @@ /** | ||
import {isDirective} from './directive.js'; | ||
import {isCEPolyfill, removeNodes} from './dom.js'; | ||
import {removeNodes} from './dom.js'; | ||
import {noChange, Part} from './part.js'; | ||
@@ -134,2 +134,12 @@ import {TemplateFactory} from './template-factory.js'; | ||
/** | ||
* Inserts this part into a container. | ||
* | ||
* This part must be empty, as its contents are not automatically moved. | ||
*/ | ||
appendInto(container: Node) { | ||
this.startNode = container.appendChild(createMarker()); | ||
this.endNode = container.appendChild(createMarker()); | ||
} | ||
/** | ||
* Inserts this part between `ref` and `ref`'s next sibling. Both `ref` and | ||
@@ -239,7 +249,2 @@ * its next sibling must be static, unchanging nodes such as those that appear | ||
const fragment = instance._clone(); | ||
// Since we cloned in the polyfill case, now force an upgrade | ||
if (isCEPolyfill) { | ||
document.adoptNode(fragment); | ||
customElements.upgrade(fragment); | ||
} | ||
instance.update(value.values); | ||
@@ -246,0 +251,0 @@ this._commitNode(fragment); |
@@ -15,8 +15,8 @@ /** | ||
import {isCEPolyfill, removeNodes} from './dom.js'; | ||
import {removeNodes} from './dom.js'; | ||
import {NodePart} from './parts.js'; | ||
import {templateFactory as defaultTemplateFactory, TemplateFactory} from './template-factory.js'; | ||
import {TemplateInstance} from './template-instance.js'; | ||
import {TemplateResult} from './template-result.js'; | ||
export const templateInstances = new WeakMap<Node, TemplateInstance>(); | ||
export const parts = new WeakMap<Node, NodePart>(); | ||
@@ -41,22 +41,10 @@ /** | ||
templateFactory: TemplateFactory = defaultTemplateFactory) { | ||
const template = templateFactory(result); | ||
let instance = templateInstances.get(container); | ||
// Repeat render, just call update() | ||
if (instance !== undefined && instance.template === template && | ||
instance.processor === result.processor) { | ||
instance.update(result.values); | ||
return; | ||
let part = parts.get(container); | ||
if (part === undefined) { | ||
removeNodes(container, container.firstChild); | ||
parts.set(container, part = new NodePart(templateFactory)); | ||
part.appendInto(container); | ||
} | ||
// First render, create a new TemplateInstance and append it | ||
instance = new TemplateInstance(template, result.processor, templateFactory); | ||
templateInstances.set(container, instance); | ||
const fragment = instance._clone(); | ||
removeNodes(container, container.firstChild); | ||
// Since we cloned in the polyfill case, now force an upgrade | ||
if (isCEPolyfill && !container.isConnected) { | ||
document.adoptNode(fragment); | ||
customElements.upgrade(fragment); | ||
} | ||
container.appendChild(fragment); | ||
instance.update(result.values); | ||
part.setValue(result); | ||
part.commit(); | ||
} |
@@ -15,5 +15,4 @@ /** | ||
import {removeNodes} from './dom.js'; | ||
import {insertNodeIntoTemplate, removeNodesFromTemplate} from './modify-template.js'; | ||
import {templateInstances} from './render.js'; | ||
import {parts, render as litRender} from './render.js'; | ||
import {templateCaches} from './template-factory.js'; | ||
@@ -43,5 +42,5 @@ import {TemplateInstance} from './template-instance.js'; | ||
console.warn( | ||
`Incompatible ShadyCSS version detected.` + | ||
`Please update to at least @webcomponents/webcomponentsjs@2.0.2 and` + | ||
`@webcomponents/shadycss@1.3.1.`); | ||
`Incompatible ShadyCSS version detected.` + | ||
`Please update to at least @webcomponents/webcomponentsjs@2.0.2 and` + | ||
`@webcomponents/shadycss@1.3.1.`); | ||
compatibleShadyCSSVersion = false; | ||
@@ -113,30 +112,26 @@ } | ||
*/ | ||
const ensureStylesScoped = | ||
const styleTemplatesForScope = | ||
(fragment: DocumentFragment, template: Template, scopeName: string) => { | ||
// only scope element template once per scope name | ||
if (!shadyRenderSet.has(scopeName)) { | ||
shadyRenderSet.add(scopeName); | ||
const styleTemplate = document.createElement('template'); | ||
Array.from(fragment.querySelectorAll('style')).forEach((s: Element) => { | ||
styleTemplate.content.appendChild(s); | ||
}); | ||
window.ShadyCSS.prepareTemplateStyles(styleTemplate, scopeName); | ||
// Fix templates: note the expectation here is that the given `fragment` | ||
// has been generated from the given `template` which contains | ||
// the set of templates rendered into this scope. | ||
// It is only from this set of initial templates from which styles | ||
// will be scoped and removed. | ||
removeStylesFromLitTemplates(scopeName); | ||
// ApplyShim case | ||
if (window.ShadyCSS.nativeShadow) { | ||
const style = styleTemplate.content.querySelector('style'); | ||
if (style !== null) { | ||
// Insert style into rendered fragment | ||
fragment.insertBefore(style, fragment.firstChild); | ||
// Insert into lit-template (for subsequent renders) | ||
insertNodeIntoTemplate( | ||
template, | ||
style.cloneNode(true), | ||
template.element.content.firstChild); | ||
} | ||
shadyRenderSet.add(scopeName); | ||
// Move styles out of rendered DOM and store. | ||
const styles = fragment.querySelectorAll('style'); | ||
const styleFragment = document.createDocumentFragment(); | ||
for (let i = 0; i < styles.length; i++) { | ||
styleFragment.appendChild(styles[i]); | ||
} | ||
// Remove styles from nested templates in this scope. | ||
removeStylesFromLitTemplates(scopeName); | ||
// And then put them into the "root" template passed in as `template`. | ||
insertNodeIntoTemplate( | ||
template, styleFragment, template.element.content.firstChild); | ||
// Note, it's important that ShadyCSS gets the template that `lit-html` | ||
// will actually render so that it can update the style inside when | ||
// needed. | ||
window.ShadyCSS.prepareTemplateStyles(template.element, scopeName); | ||
// When using native Shadow DOM, replace the style in the rendered | ||
// fragment. | ||
if (window.ShadyCSS.nativeShadow) { | ||
const style = template.element.content.querySelector('style'); | ||
if (style !== null) { | ||
fragment.insertBefore(style.cloneNode(true), fragment.firstChild); | ||
} | ||
@@ -146,5 +141,2 @@ } | ||
// NOTE: We're copying code from lit-html's `render` method here. | ||
// We're doing this explicitly because the API for rendering templates is likely | ||
// to change in the near term. | ||
export function render( | ||
@@ -154,29 +146,20 @@ result: TemplateResult, | ||
scopeName: string) { | ||
const templateFactory = shadyTemplateFactory(scopeName); | ||
const template = templateFactory(result); | ||
let instance = templateInstances.get(container); | ||
// Repeat render, just call update() | ||
if (instance !== undefined && instance.template === template && | ||
instance.processor === result.processor) { | ||
instance.update(result.values); | ||
return; | ||
const shouldScope = | ||
container instanceof ShadowRoot && compatibleShadyCSSVersion; | ||
const hasScoped = shadyRenderSet.has(scopeName); | ||
// Call `styleElement` to update element if it's already been processed. | ||
// This ensures the template is up to date before stamping the template | ||
// into the shadowRoot *or* updates the shadowRoot if we've already stamped | ||
// and are just updating the template. | ||
if (shouldScope && hasScoped) { | ||
window.ShadyCSS.styleElement((container as ShadowRoot).host); | ||
} | ||
// First render, create a new TemplateInstance and append it | ||
instance = new TemplateInstance(template, result.processor, templateFactory); | ||
templateInstances.set(container, instance); | ||
const fragment = instance._clone(); | ||
instance.update(result.values); | ||
// If there's a shadow host, do ShadyCSS scoping... | ||
if (container instanceof ShadowRoot && compatibleShadyCSSVersion) { | ||
ensureStylesScoped(fragment, template, scopeName); | ||
window.ShadyCSS.styleElement(container.host); | ||
litRender(result, container, shadyTemplateFactory(scopeName)); | ||
// When rendering a TemplateResult, scope the template with ShadyCSS | ||
if (shouldScope && !hasScoped && result instanceof TemplateResult) { | ||
const part = parts.get(container)!; | ||
const instance = part.value as TemplateInstance; | ||
styleTemplatesForScope( | ||
(container as ShadowRoot), instance.template, scopeName); | ||
} | ||
removeNodes(container, container.firstChild); | ||
container.appendChild(fragment); | ||
} |
@@ -109,2 +109,6 @@ /** | ||
_prepareInstance(fragment); | ||
if (isCEPolyfill) { | ||
document.adoptNode(fragment); | ||
customElements.upgrade(fragment); | ||
} | ||
return fragment; | ||
@@ -111,0 +115,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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
330992
15
4793