lit-html
Version:
HTML template literals in JavaScript
145 lines • 5.36 kB
JavaScript
/**
* @license
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
import { AttributePart, defaultPartCallback, noChange, getValue, SVGTemplateResult, TemplateResult } from '../lit-html.js';
export { render } from '../lit-html.js';
/**
* Interprets a template literal as a lit-extended HTML template.
*/
export const html = (strings, ...values) => new TemplateResult(strings, values, 'html', extendedPartCallback);
/**
* Interprets a template literal as a lit-extended SVG template.
*/
export const svg = (strings, ...values) => new SVGTemplateResult(strings, values, 'svg', extendedPartCallback);
/**
* A PartCallback which allows templates to set properties and declarative
* event handlers.
*
* Properties are set by default, instead of attributes. Attribute names in
* lit-html templates preserve case, so properties are case sensitive. If an
* expression takes up an entire attribute value, then the property is set to
* that value. If an expression is interpolated with a string or other
* expressions then the property is set to the string result of the
* interpolation.
*
* To set an attribute instead of a property, append a `$` suffix to the
* attribute name.
*
* Example:
*
* html`<button class$="primary">Buy Now</button>`
*
* To set an event handler, prefix the attribute name with `on-`:
*
* Example:
*
* html`<button on-click=${(e)=> this.onClickHandler(e)}>Buy Now</button>`
*
*/
export const extendedPartCallback = (instance, templatePart, node) => {
if (templatePart.type === 'attribute') {
if (templatePart.rawName.substr(0, 3) === 'on-') {
const eventName = templatePart.rawName.slice(3);
return new EventPart(instance, node, eventName);
}
const lastChar = templatePart.name.substr(templatePart.name.length - 1);
if (lastChar === '$') {
const name = templatePart.name.slice(0, -1);
return new AttributePart(instance, node, name, templatePart.strings);
}
if (lastChar === '?') {
const name = templatePart.name.slice(0, -1);
return new BooleanAttributePart(instance, node, name, templatePart.strings);
}
return new PropertyPart(instance, node, templatePart.rawName, templatePart.strings);
}
return defaultPartCallback(instance, templatePart, node);
};
/**
* Implements a boolean attribute, roughly as defined in the HTML
* specification.
*
* If the value is truthy, then the attribute is present with a value of
* ''. If the value is falsey, the attribute is removed.
*/
export class BooleanAttributePart extends AttributePart {
setValue(values, startIndex) {
const s = this.strings;
if (s.length === 2 && s[0] === '' && s[1] === '') {
const value = getValue(this, values[startIndex]);
if (value === noChange) {
return;
}
if (value) {
this.element.setAttribute(this.name, '');
}
else {
this.element.removeAttribute(this.name);
}
}
else {
throw new Error('boolean attributes can only contain a single expression');
}
}
}
export class PropertyPart extends AttributePart {
setValue(values, startIndex) {
const s = this.strings;
let value;
if (this._equalToPreviousValues(values, startIndex)) {
return;
}
if (s.length === 2 && s[0] === '' && s[1] === '') {
// An expression that occupies the whole attribute value will leave
// leading and trailing empty strings.
value = getValue(this, values[startIndex]);
}
else {
// Interpolation, so interpolate
value = this._interpolate(values, startIndex);
}
if (value !== noChange) {
this.element[this.name] = value;
}
this._previousValues = values;
}
}
export class EventPart {
constructor(instance, element, eventName) {
this.instance = instance;
this.element = element;
this.eventName = eventName;
}
setValue(value) {
const listener = getValue(this, value);
if (listener === this._listener) {
return;
}
if (listener == null) {
this.element.removeEventListener(this.eventName, this);
}
else if (this._listener == null) {
this.element.addEventListener(this.eventName, this);
}
this._listener = listener;
}
handleEvent(event) {
if (typeof this._listener === 'function') {
this._listener.call(this.element, event);
}
else if (typeof this._listener.handleEvent === 'function') {
this._listener.handleEvent(event);
}
}
}
//# sourceMappingURL=lit-extended.js.map