Code Monkey home page Code Monkey logo

lit's Introduction

Lit

Simple. Fast. Web Components.

Build Status Published on npm Join our Discord Mentioned in Awesome Lit

Lit is a simple library for building fast, lightweight web components.

At Lit's core is a boilerplate-killing component base class that provides reactive state, scoped styles, and a declarative template system that's tiny, fast and expressive.

Documentation

See the full documentation for Lit at lit.dev.

Additional documentation for developers looking to contribute or understand more about the project can be found in dev-docs.

npm

To install from npm:

npm i lit

Lit Monorepo

This is the monorepo for Lit packages.

lit 2.x source is available on the 2.x branch. lit-html 1.x source is available on the lit-html-1.x branch.

Packages

Contributing to Lit

Lit is open source and we appreciate issue reports and pull requests. See CONTRIBUTING.md for more information.

Setting up the lit monorepo for development

Initialize repo:

git clone https://github.com/lit/lit.git
cd lit
npm ci

Build all packages:

npm run build

Test all packages:

npm run test

Run benchmarks for all packages:

npm run benchmarks

See individual package READMEs for details on developing for a specific package.

lit's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lit's Issues

bindings in <template> not working

const who = 'world';
render(html`<template id="res">hello ${who}</template>`, document.body);

// `hello {{}}` instead of `hello world` :(
console.log(document.getElementById('res').innerHTML);

Async Iterable Support

Now that there's a PR for Promises (#14), we should think about async iterables.

Async iterables would allow for very easy to express incrementally rendered lists. To control how fast items are rendered into the list, just control how values are resolved into the iterable. Streaming server responses could also easily be rendered incrementally.

The part that's trickier over Promise support is that we can't just call setValue() in the callback anymore, we have to append new item Parts, and might have to give up on part reuse if it's too complex.

Did you mean ```static observedProperties = ['title', 'body'];``` here?

Se below

class MyElement extends CoolLitMixin(HTMLElement) {

  static observedProperties = ['message', 'name'];

  title = `About lit-html`;
  body = `It's got potential.`;

  // Called by the base-class when properties change, result is passed
  // to lit-html's render() function.
  render() {
    return html`
      <h1>${this.title}</h1>
      <p>${this.body}</p>
    `;
  }
}

Unable to remove a listener

This line makes it impossible to reach the listener === undefined one.

Apparently there are no tests to cover listeners (add/remove) in the extended version.

Consider splitting up lit-extended

Hey! Great library, I've very excited about it. I was wondering if you would be open to splitting up lit-extended into the PropertyPart and EventPart functionality. I would like to use EventPart but not PropertyPart (instead of requiring a special syntax for attributes I'd rather use an in check personally). Not sure how easy this would be to do... it's small enough code that I might just copy/paste into my own project if it's too much work to break these apart.

Template keeps getting recreated for ES5 / ES3 targets (Typescript)

Recently I tried to create a simple greeter component with an input that greets the person the user inputs. I used Typescript.

Bascially,

function myComponent(whom: string) {
    return html`Hello <input on-input=${() => updateWhom}>  ${whom}!`;
}

Where "updateWhom“ just re-renders the component with the new name

function updateWhom(e: Event) {
    let target = e.target as HTMLInputElement;
    render(myComponent(target.value), app);
}

I bundled the application using webpack and on modern browsers, it works as intended - when a user types "Bob" into the input, "Hello Bob!" is output.

Now, changing tsconfig’s "target“ from "es2015“ to "es5“ for browser compatibility changes the behavior of the application.

Whenever the user inputs something into the input, the input element loses focus after every keystroke. The greeter text still gets updated to the single character that was typed.
It seems this is because the entire Component, including the input element, gets completely recreated

Possible Explanation

The typescript compiler compiles the "myComponent“ method to

function myComponent(whom) {
return (_a = ["Hello <input on-input=", ">  ", "!"], _a.raw = ["Hello <input on-input=", ">  ", "!"], html(_a, function () { return updateWhom; }, whom));
var _a;
}

While this is correct and will call the "html" function with the same arguments as a tagged template would, it will create a new strings array (_a) on each call to "myComponent".

This breaks the "html" function, because it looks up the strings array in a Map:

function html(strings, ...values) {
    let template = templates.get(strings);
    if (template === undefined) {
        template = new Template(strings);
        templates.set(strings, template);
    }
    return new TemplateResult(template, values);
}

With real tagged templates, the "strings" parameter will always refer to the same array, so that in subsequent calls to the "html" function with the same tagged template, "strings" will be found in the map and so the Template that's saved in the map gets used as intended.

But in the compiled code, a different array gets passed in "strings" every time that just happens to have the sme content, so the template is never found in the map and a new template has to be created every time that destroys the old HTML elements.

I'd say this isn't really lit-html's fault, but Typescript's.
They should probably instead compile it like

var _tmp = ["Hello <input on-input=", ">  ", "!"];
_tmp.raw = _tmp.splice;

function myComponent(whom) {
    return html(_tmp, function () { return updateWhom; }, whom);
}

(or similar) so the same exact arrays are reused, just like they are with tagged templates.

Babel actually handles this correctly:

var _templateObject = _taggedTemplateLiteral(["Hello <input on-input=", ">  ", "!"], ["Hello <input on-input=", ">  ", "!"]);

function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }

function myComponent(whom) {
    return html(_templateObject, function () {
        return updateWhom;
    }, whom);
}

Static text content of node does not change when re-rendered

Consider the following contrived usage of lit:

const container = document.createElement('div');

html`<div>foo</div>`.renderTo(container);
html`<div>bar</div>`.renderTo(container);

What I expected

<div>
  <div>bar</div>
</div>

What I got

<div>
  <div>foo</div> <!-- The text content didn't change! -->
</div>

Problem with event subscription when not using fat arrow syntax (lit-extended)

Version: [email protected] - lit-extended

If I subscribe to an event like this everything works great:

WORKS: <button on-click=${ e => this._reverseClicked(e) }>Reverse</button>
DOES NOT WORK: <button on-click=${ this._reverseClicked }>Reverse</button>

However, if I use the abbreviated syntax because I do not care about the event, this will be undefined and I will get an error. Here is my very complicated reverse function for reference:

_reverseClicked(event) { this.message = this.message.split('').reverse().join(''); }

Obviously, I will stick with the fat arrow syntax for now.

Allow injection of static content into templates

As discussed in this thread, it would be useful to have some way of injecting static content into the templates that would be used at compile time of the templates, but not change per-render.

Potential syntax that come out of that thread would be using a utility function to demarcate static content:

import { html, unsafeStatic } from 'lit-html'
import SomeElement from 'some-element';

const elementName = unsafeStatic(SomeElement.is);
let dynamicTagTemplate = html`<${elementName}>Hello</${elementName}>`

/cc @justinfagnani let me know if I misinterpreted that syntax you suggested

Release with javascript build

It's completely unusable and inaccessible currently, or i'm an idiot :D In package.json don't have files field and only expose main that is to not existing file.

Currently i clone, compile with TS and then build cjs with rollup (because i'm trying it using budo server so it need cjs). But in anyway, the TS compiled 5 to some strange thing NodeFilter.SHOW_TEXT and everything fails. That okey, i debugged everything and replaced this with 5 because in the source code is 5 (here, and here)

That 5 /* elements & text */ was compiled to NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT)

But when trying to build lit-extended Rollup fails with magicString of undefined error, which is error that was happening before updating the mentioned above.

Optimizations

Tracking issue for optimization ideas we want to try when we have time.

  • - Change template instantiation for first render: Instead of TemplateInstance.new(), _clone(), update(), where clone imports the template, then does a tree walk of the clone to index the parts, then update() sets values, causing a bunch of insertNodes, combine into one method and do a tree walk of the template creating static nodes and parts values in one pass. This assumes appendChild is faster than insertNode.
  • - Dirty-checking for simple values. This should make updates way faster. PR #35
  • - Change the Part.setValue() API to take a list of values and an index to start from. This eliminates a slice() call. PR #42
  • - Eliminate the near-full tree walk on instance first-render (alternate to first optimization). Maybe use childNodes like Polymer? PR #50
  • - Optimize for NodeParts that are the only child. They don't need start and end marker nodes.
  • - Micro-optimizations around ordering of if/else cascades, instanceof, etc.

Tests don‘t test anything

The tests currently don’t run at all.

Adding -p to the wct invocation shows that some libraries don’t load and as a result no tests are being run.

screen shot 2017-08-31 at 12 06 45 pm

Question: How to handle render inheritance?

What's the proper way to handle inheritance with this library?

Multiple:

class Base {
  render () {
    return html`<div></div>`
  }
}

class Derived extends Base {
  render () {
    return html`<span>${super.render()}</span>`
  }
}

const derived = new Derived()
render(derived.render(), document.body) // lit-html's render

Single v1:

class Base {
  render () {
    return `<div></div>`
  }
}

class Derived extends Base {
  render () {
    return html`<span>${super.render()}</span>`
  }
}

const derived = new Derived()
render(derived.render(), document.body) // lit-html's render

Single v2:

class Base {
  render () {
    return `<div></div>`
  }
}

class Derived extends Base {
  render () {
    return `<span>${super.render()}</span>`
  }
}

const derived = new Derived()
render(html`${derived.render()}`, document.body) // lit-html's render

Now that I think about it, I suspect that the multiple example won't actually work, but would love some confirmation.

Render DOM from a function call

I'm wondering how to get something as simple as the following to work:

render(html`
            ${() => {
                return `<div>Hello</div>`;
            }}
`, document.getElementById('render-area'));

I would like the div to actually be a DOM div, but it just returns a string and renders as a literal string. Am I missing something simple? This will also help me figure out how to do lists like dom-repeat or how JSX does them. Thanks.

when vs if

When is called if in JS, so I find it a bit weird to have to invent another term

const render = () => html`
  ${when(state === 'loading',
    html`<div>Loading...</div>`,
    html`<p>${message}</p>`)}
`;

In general, I think people will keep preferring JSX, because there they could just use actual JS (if, map etc) and mix and match it with HTML.

hyperHTML does something similar to when but used the [] format instead:

(it requires a wire() method call though)

https://viperhtml.js.org/hyperhtml/examples/#!fw=React&example=Conditional%20Rendering
https://github.com/WebReflection/hyperHTML#wait--there-is-a-wire--in-the-code

hyperHTML seems to be able to use map for collections as well:

function update(render, state) {
  render`
  <article data-magic="${state.magic}">
    <h3> ${state.title} </h3>
    List of ${state.paragraphs.length} paragraphs:
    <ul>${
      state.paragraphs
        .map(p => `<li>${p.title}</li>`)
    }</ul>
  </article>
  `;
}

Can you do the same with lit-html?

Benchmarks

We need some basic rendering benchmarks to make sure we don't regress, and measure any improvements.

Long term vision

As a long time user of Polymer, this project expresses exactly what I feel is missing from our toolbox when building apps in Polymer.

What's the long term vision for this, and it's relation to Polymer? Is it something that will be officially supported? I'd hate to see this end up gathering dust.

IE / Edge are weakly supported

Try the following on any IE / Edge browser.

render(
  html`<i data-i="${'a'}" class="${'b'}"></i>`,
  document.body
);
document.body.appendChild(
  document.createTextNode(
    document.body.firstElementChild.outerHTML
  )
);

Expected layout

<i data-i="a" class="b"></i>

Current layout

<i class="a" data-i="b"></i>

screenshot from 2017-08-23 15-05-18

FYI this is fixed in hyperHTML since v0 and Firefox used to have the same issue.

List tail not removed on subsequent renders

Consider the following contrived usage of lit-html:

function ul(list) {
  var items = list.map(item => html`<li>${item}</li>`);
  return html`<ul>${items}</ul>`;
}

ul(['a', 'b', 'c']).renderTo(document.body);
ul(['x', 'y']).renderTo(document.body);

What I expected

<body>
  <ul>
    <li>x</li>
    <li>y</li>
  </ul>
</body>

What I got

<body>
  <ul>
    <li>x</li>
    <li>y</li>
    <li>c</li> <!-- This item should have been removed -->
  </ul>
</body>

Better ergonomics around Function values

Function values are a sharp edge of the API. It's very easy to pass a function to a template expecting it to be used as an event handler, when instead it's invoked immediately as a template extension.

One possible way around this is to not invoke functions during rendering unless they have a special property, which could be set by a utility function:

import { directive } from '../lit-html.js';

export repeat = directive((part: NodePart) => { ... }

Since function literals written by template authors are probably going to be written many, many times more than directives, it makes sense to optimize for their experience.

The one downside it this makes guarded (exception-safe) expressions longer. We'd have to vend a directive that ran a function immediately in a try/catch block:

html`<input value=${safe(_=> this.foo.bar)}></input>`;

Allow conditional creation of attributes

lit currently doesn't allow you to conditionally create an attribute.

For example, if you would like to toggle 'checked' state on an input.

There is no way to conditionally toggle the 'checked' attribute.

This may be related to #56 but that seems to expect undefined to not serialize, but does not mention removing attributes.

Adding functions to events seems to be executed immediately

Hi! 💯

I'm trying this lib, and in first look it seems fantastic and works. Works, but almost. It's seems that when attach an event handler it is executed immediately. For example setting a function on onclick event is called on render, not when element is clicked.

const msg = 'Hello!'
const Main = () => html`<main>
  <h1>${msg}!</h1>
  <button onclick=${() => {  console.log('woohoo') }}>+1</button>
</main>`

render(Main(), document.body)

http://jsbin.com/sogitix/28/edit?js,console,output

No partial table

As explained in this comparison point, lit-html does not support partial tables layout.

render(
  html`
  <table>
    ${[[1, 2], [3, 4]].map(tr =>
      html`<tr>
      ${tr.map(text => html`<td>${text}</td>`)}
    </tr>`)}
  </table>`,
  document.body
);

produces

<tr></tr>
<td>1</td><td>2</td>
<tr></tr>
<td>3</td><td>4</td>
<table></table>

Beginners question (probably)

When copying and pasting in the examples into an html file and running it in a browser (chrome version 60) and of course linking to the transpiled version (lit-html.js). Chrome reports errors about the export keyword not being recognized.. (SyntaxError: Unexpected token export)..

Even when running it in NodeJS by just require('lit-html') after npm installing it shows this same error...

What am I missing here?

Up to date docs

hey guys,

Following this project with interest. Just wondering if for every new PR/feature you'll have updated the docs, too?

Noticed the until() PR was merged but could not find the docs. Maybe I missed it?

Wrong description in readme

render() takes a TemplateResult and renders it to a DOM container. On the initial render it clones the template, then walks it using the remembered placeholder positions, to create Parts.

Actually html() created the Template which calls _parse and does the walking

renderExtendedTo does not appear to reuse existing nodes

Consider the following contrived usage of lit's experimental renderExtendedTo function:

function ul(list) {
  var items = list.map(item => html`<li>${item}</li>`);
  return html`<ul>${items}</ul>`;
}

renderExtendedTo(ul(['a', 'b', 'c']), document.body);
renderExtendedTo(ul(['x', 'y']), document.body);

What I expected

<body>
  <ul>
    <li>x</li>
    <li>y</li>
  </ul>
</body>

What I got

<body>
  <ul>
    <li>a</li>
    <li>b</li>
    <li>c</li>
  </ul>

  <ul>
    <li>x</li>
    <li>y</li>
  </ul>
</body>

Event handler context incorrect

Here's some example code:

import {html} from './node_modules/lit-html/lib/lit-html';
import {render} from './node_modules/lit-html/lib/labs/lit-extended';

class TestElement extends HTMLElement {
    connectedCallback() {
        render(html`
            <button onclick="${() => this.buttonClick}">Property listener</button>
            <button on-click="${() => this.buttonClick}">Custom syntax listener</button>
        `, this);
    }

    buttonClick() {
        this.helloWorld();
    }

    helloWorld() {
        console.log('Hello world!');
    }
}

window.customElements.define('test-element', TestElement);

If you click either of the buttons, buttonClick is invoked, but the context is set to the button, and not to the host component. Obviously this does not allow this.helloWorld to be called.

repeat() fails when it rendered conditionally

I’m not sure this is a bug, but consider this test case I wrote:

    test('can be rendered conditionally ', () => {
      let flag = false;
      const items = [1, 2, 3];
      const t = () => html`${flag ? repeat(items, (i: number) => html`<li>item: ${i}</li>`) : ''}`;
      render(t(), container);
      assert.equal(container.innerHTML, ``);
      flag = true;
      render(t(), container);
      assert.equal(container.innerHTML, `<li>item: 1</li><li>item: 2</li><li>item: 3</li>`);
    });

I know this can be fixed by moving the conditional to the iterable like flag ? items : [] or something, but I am wondering if this should work too.

CSS containment

There was a drive to enable some good default for CSS containment for custom elements and element in general, but it was rejected as far as I recall, due to backwards compatibility.

Maybe it makes sense to reconsider that for lit html?

No SVG utility

There is currently no way to create SVG nodes instead of HTML nodes.

render(html`<rect />`, document.body);
'ownerSVGElement' in document.body.firstElementChild; // false

maybe an svg tag is needed?

Syntax error in readme array example

The Arrays/Iterables section has this code example, which contains a syntax error due to a missing backtick

const render = () => html`items = ${items.map((i) => `item: ${i})}`;

repeat() fails on multiple rerender

Here’s a test case:

    test('can rerender multiple times', () => {
      let items = [1, 2, 3, 4, 5];
      const t = () => html`${repeat(items, (i: number) => html`
            <li>item: ${i}</li>`)}`;
      render(t(), container);
      render(t(), container);
      render(t(), container);
      render(t(), container);
      assert.equal(container.innerHTML, `<li>item: 1</li><li>item: 2</li><li>item: 3</li><li>item: 4</li><li>item: 5</li>`);
    })

Error:
screen shot 2017-09-04 at 2 31 30 pm

CSS Variables

I've got this CSS variable I'm trying to use from within a template literal:

:host {
    --box-shadow: 0px 0px 1px black;
}

The variable is not applied to the host element when inspecting it with the Chrome dev tools.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.