lit / lit-element

LEGACY REPO. This repository is for maintenance of the legacy LitElement library. The LitElement base class is now part of the Lit library, which is developed in the lit monorepo.
https://lit-element.polymer-project.org
BSD 3-Clause "New" or "Revised" License
4.49k stars 319 forks source link

Ideas for LitElement 3.0 #1077

Closed sorvell closed 2 years ago

sorvell commented 3 years ago

LitElement 2.x has been out for about a year, and we’ve been considering what changes we should make for the next major version. We wanted to share our thoughts and get feedback from our users about potential improvements and breaking changes.

Overall, we’re generally pretty satisfied with the core functionality and API and we definitely want to keep in mind that dealing with breaking changes is a hassle for users. We do, however, think there are some good opportunities for improvement. The lit-html library is also considering changes for the next major version, and we’ll definitely be including the next version of lit-html with the next version of LitElement.

Goals

Backwards compatibility for most users

We know that dealing with breaking changes is annoying and we’re trying to carefully manage our change budget such that each change has either a big return on investment or is likely to affect very few users. If necessary, we may elect to create a compatibility version that maintains 2.x behavior if it makes sense.

Performance

We love performance and are always striving to improve it. The next version of lit-html has a number of performance improvements from which LitElement will naturally benefit.

Size reductions

There’s some good opportunity for improvement in the core size of LitElement. First, the next version of lit-html should be getting a good bit smaller. Then, there’s a good bit of refactoring we can do to help make sure users are only getting the code that they use, including making old browser support opt-in, making decorators opt-in and more granular, and streamlining some of our internal API.

In addition, we may publish a minified, bundled build so that we can ensure that a heavily minified version works correctly. While we believe minification is ideally an app concern, we realize that apps won't know all the transforms that can be safely applied to our code.

Developer Experience

We want to make sure LitElement is easy to use for all our users. We know that some web devs love the pure experience of downloading some code, editing in a text editor, and seeing the output in a browser and we want to make sure that we support that use case better.

We would like to improve error messages, and not let publishing minified code negatively impact development. We'll look into producing both production and development builds.

Easier SSR

We know users want SSR, both for SEO and first paint performance. By leveraging the support for SSR being built into the next version of lit-html, LitElement will support SSR and it will be able to take advantage of the declarative Shadow DOM feature being added to Chrome.

New Features

While we want to consider some feature additions, we’re pretty happy with the core functionality. One thing we’d like to explore is providing a package of helpers for features not everyone needs but are really helpful for specific use cases or are just one way to skin the cat.

Fix bugs that require minor breaking changes

We have a number of longstanding minor issues that we haven’t been able to fix because they are just slightly breaking changes. We’d like to evaluate those and address what we can.

API cleanup

We think there’s a number of opportunities to reduce code size by carefully sweeping through the API. This should mostly be internal changes, but there may be some minor changes to the public facing API where we think there’s a strong benefit.

Potential Changes

Opt-in polyfill support

Although the Shady DOM polyfill itself is opt-in, there is some code baked into LitElement to use the polyfill that all users must pay for, which adds close to 1KB (~15%) to the bundle size. We’d like to make this opt-in so users who don’t need to support older browsers don’t pay the cost of downloading the polyfill specific code.

Opt-in and granular decorators

LitElement's main module exports all of the decorators we support regardless of whether or not they are used. We will break these out into granular modules that users will import if they use. This will also allow us to more freely add new decorators where useful.

Package that works out of the box without building

For optimal code size a build will always be best, but we want to provide an easy way to try LitElement without using npm and building the code.

Guidance for use in popular frameworks

The main value proposition of web components is that they work everywhere the (modern) web works. That’s true but they should also work seamlessly with modern web frameworks as well. We want to make sure LitElements work great in popular frameworks like React, Vue, and Angular. As shown on custom elements everywhere, the situation today is pretty good, and we may only need to create a set of examples showing what’s possible. However, we will be considering patterns and helpers that can make things easier for framework users.

Code sharing primitive

We think that subclassing and mixins cover most of the cases where users want to share code between elements. However, there are some cases they don’t cover. We’ve been impressed by efforts in the community to address those use cases, in particular React Hooks and the Vue composition API. We think there’s an opportunity to do something similar but probably a lot simpler for LitElement. This might take the form of controllers that can bundle functionality via a has-a relationship with the element and that can interact with the lifecycle of LitElement. Since not everyone needs this feature, this is probably a good candidate for a helper package.

SSR Support

When SSR output is being generated we need to consider which parts of the element lifecycle to run. It’s a goal of our SSR implementation not to require a server side DOM implementation and therefore code that uses DOM APIs will have no or very limited support. We will likely be modifying the update cycle for the element when it’s run on the server to avoid methods which often perform DOM manipulation, for example the firstUpdated and updated methods.

Theming

Platform support for theming is still incomplete. CSS custom properties are great for tree-based theming of known sets of properties. The new ::part selector works well for arbitrary theming of specific elements. However, the exportparts attribute is a cumbersome way to expose parts up the tree. In addition, it’s often desirable to allow users to style a set of unknown properties but not everything. There are a lot of patterns that can help here and we may end up supporting a few of them, perhaps in a helper package. We like the Vaadin themable-mixin and something like that for LitElement may make sense. We also like the power and configurability of Sass mixins and may explore exposing something similar via a Javascript API.

Declarative events

Declaring what events your element fires is great for self-documenting. We can also follow the DOM convention of providing an on prefixed property to listen to the event. Having on prefixed properties also improves integration with frameworks like React that configure events this way. We will probably implement this as a decorator but will have some form of raw JS support as well. See this issue for more info.

Common customization like shadowRoot rendering

To customize how the element’s shadowRoot is created you currently have to override createRenderRoot. We’d like to make this a bit easier and may add a decorator or static field with which you can supply the options object to attachShadow. For example:

static shadowRootOptions = {mode: ‘open’, delegatesFocus: true}

We may also explore this for other common but simple to configure customization points.

Declarative host manipulation

While we use lit-html to render the element’s Shadow DOM, attributes and event listeners on the hosting element itself must be added imperatively. We may be able to use some of the new features in lit-html 2.x to create a declarative way of doing this. This might take the form of a renderHost method which returns a lit-html TemplateResult that’s rendered on the host element. See this issue for more info.

Update coordination

LitElement updates asynchronously at microtask timing, and while this can’t be observed by the user, it can be by code. After an element has been interacted with, it’s sometimes useful to know when it’s finished rendering; for example, this is useful in testing, when firing events, or when you need to know the element’s rendering is stable. Although this can also be used for measuring DOM, it’s usually better to measure in a requestAnimationFrame callback. LitElement provides the updateComplete promise for these use cases. However, this only covers the element itself and not any LitElements in its shadowRoot. To wait for those elements, typically you override the updateComplete property and await those elements as well. That’s a bit cumbersome, and we want to explore adding a helper that makes this easier. Since this is only sometimes needed, this might be another feature which gets added to a helper package.

Christian24 commented 3 years ago

Excited for this. A couple things that came to my mind:

jaredcwhite commented 3 years ago

Oooo, lots of good stuff to chew on here… I'm particularly interested in the "Code sharing primitive" angle, as it something I was thinking about recently as well. I did a lot of StimulusJS work before becoming enamored with LitElement, and one advantage Stimulus has is you can attach multiple controllers to a single element. It would be cool if I could explore an analogous pattern in the LitElement world…

justinfagnani commented 3 years ago

@Christian24 lit/lit-element#469 is already addressed in 3.0. requestUpdate() does not return a Promise anymore, and you have to specifically call await this.updateComplete.

busynest commented 3 years ago

may we have good documentation on bundling large apps, and coding exercises / challenges.

funglaub commented 3 years ago

A functional approach such as react hooks would really be nice.

maartenst commented 3 years ago

may we have good documentation on bundling large apps, and coding exercises / challenges.

I think that's already taken care of by open-wc?

Westbrook commented 3 years ago

How early do you think we could start to see tech specs on the controller proposal mentioned herein? It sounds like a great tool for empowering both early LitElement learning as well as advanced/shared application development. I’d love to see whether you’d see it deeply integrated with LitElement, a pair to lit-html, or something more abstractly applicable to UI development at large.

justinfagnani commented 3 years ago

@funglaub

A functional approach such as react hooks would really be nice.

We feel that we already have a pretty functional approach with the render() method of LitElement.

There's a lot of dislike of classes out there, but for this case where we 1) are creating objects by nature of the DOM and 2) do need to declare properties and attributes, classes are actually more declarative than a "pure" functional API where attributes and properties must be declared without the advantage of native syntax for it.

justinfagnani commented 3 years ago

@Westbrook

How early do you think we could start to see tech specs on the controller proposal mentioned herein?

I'll try to publish an issue today.

ghost commented 3 years ago

Exciting ! :)

On Fri, Sep 11, 2020, 11:28 AM Justin Fagnani notifications@github.com wrote:

@Westbrook https://github.com/Westbrook

How early do you think we could start to see tech specs on the controller proposal mentioned herein?

I'll try to publish an issue today.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Polymer/lit-element/issues/1077#issuecomment-691162521, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFSDL2W6OPJSKVVVSCGAXI3SFI62JANCNFSM4RDTWGKQ .

tsavo-vdb-knott commented 3 years ago

@Westbrook https://github.com/Polymer/lit-html/pull/1246

jordanfinners commented 3 years ago

I really like LitElement, so thank you in advance!

I think it would be awesome to see like a showcase of who else/what companies are also using it as this would be great publicity and help advocate its usage in organisations.

I think more demos/starter kits, already mentioned is how to use it with other frameworks but I think examples of large applications/advanced usage would be great and also starter kits that can generate the boilerplate for standard usages e.g. with an app shell, routing, linting, testing etc. Would be happy to contribute to these!

kmmbvnr commented 3 years ago

Replace html templates with compiled js. Like Svelte and solid-js

justinfagnani commented 3 years ago

@kmmbvnr

Replace html templates with compiled js. Like Svelte and solid-js

Any reason why?

kmmbvnr commented 3 years ago

@justinfagnani

Any reason why?

Performance ?

timonweb commented 3 years ago

It would be great to get support for <slot> or an equivalent for the light dom. If I'm not mistaken, Stencil supports this. The thing is, you don't always need or want shadow dom, but the concept of slots is still very handy.

bennypowers commented 3 years ago

It could be nice to streamline this kind of pattern with decorator options

class WaitsForChildren extends LitElement {
  render() {
    return html`
      <x-l id="a"></x-l>
      <x-l id="b"></x-l>
    `;
  }

  async _getUpdateComplete() {
    await super._getUpdateComplete();
    await Promise.all(Array.from(this.shadowRoot.querySelectorAll('x-l'), x => x.updateComplete);
  }
}

class WaitsForChildren extends LitElement {
  @query('#a', { awaitUpdate: true }) a: XL;
  @query('#b', { awaitUpdate: true }) b: XL;
  render() {
    return html`
      <x-l id="a"></x-l>
      <x-l id="b"></x-l>
    `;
  }
}
eavichay commented 3 years ago
class CustomInput extends LitMixin(HTMLInputElement) { ... }

// or

@builtIn(HTMLInputElement)
class CustomInput extends LitElement { ... }

// or any other solution to reconstruct the prototype chain
bengfarrell commented 3 years ago

One thing I've been running into a bunch lately is when I have a Lit component that requires me to know my own component bounds as part of the render (I'm absolutely positioning elements), there's no great update mechanism. In fact it was kind of terrible previously, but at least now I can listen to ResizeObserver. It might be nice to have some set of bindable properties that the user doesn't change, but they can opt into that trigger requestUpdate.

For example:

@property({ type: Number, internal: 'bounds.width }) protected componentWidth: number;

Bounds of course would be from this.getBoundingClientRect.

I'm not married to the my implementation suggestion by any means, but all the same, if it was similarly implemented, it could open the door to anything that you might add-on here if there was an API to do so (timers, sensors, data request results, etc)

Honestly, this type of functionality probably doesn't have to be part of Lit, but I'm kinda sick of re-writing ways to handle this. So if not Lit core, some kind of companion util might nice

justinfagnani commented 3 years ago

@bengfarrell I think this might be nicely addressed with what I've been calling recently "reactive controllers" - helper objects that are able to hook and trigger the host element's lifecycle.

Imagine a controller called BoundsController which installs a ResizeObserver and on resize pulls the bounding rect and triggers an update. It'd be useable like:

class MyElement extends LitElement {
  private _bounds = new BoundsController(this);

  render() {
    return html`<pre>width: ${this._bounds.width}, height: ${this._bounds.height}</pre>`
  }
}

Note, this is pretty easily writable today - it doesn't require any new APIs on LitElement.

bengfarrell commented 3 years ago

@justinfagnani I think that's exactly the type of thing I was steering towards if I put more thought into it. And yes, it IS easily writeable today, but as a user the design pattern didn't really occur to me until I thought about what I was missing. And this is why I'm wondering if a few reactive controllers could be shipped alongside Lit (just import when you need it, not part of the core lib), in the same way you're offering extra directives. The few that ship could solve some common problems like bounds, but more importantly offer guidance to create your own. Of course, this might fall under the category of "too opinionated" a design pattern to ship with Lit

tvvignesh commented 3 years ago

@justinfagnani CustomElements spec allow you to register a component only once which is very much sensible but it would be great if the decorator @customElement('my-element') can skip declaration if it is already in the registry instead of failing with an error.

Currently I am doing customElements.get('my-element') || customElements.define('my-element', MyElement); to support this.

This is relevant because I use storybook for demoing and strangely, when you include same components in multiple different stories, they get registered multiple times throwing errors in the storybook.

A similar issue in a diffent context was raised here I guess: https://github.com/Polymer/lit-element/issues/207

tvvignesh commented 3 years ago

Another thing which may not be relevant to this context is that, while everything is awesome, VSCode support is limiting. There are 1 or 2 extensions but they don't support everything like css language support within the css tags (or am I doing something wrong?)

1

Also, there is no syntax highlighting in this case 😢

Infact, I am even wondering if it is recommended to put the .css file separately or write css within the css tags.

PS: The <style> tag within html works well though but I just use html tag for html and css tag for css

tvvignesh commented 3 years ago

Also, for people who are starting off something which you guys did like PWA helpers (https://github.com/Polymer/pwa-helpers) for lit would be great. While open-wc has https://open-wc.org/developing/lit-helpers.html it may either need more additions to it or you may want to take it up. For eg. the recommended way to do routing (with each element handling its own routing) would be great. But, this is not a must have and not related to lit 3.0 either, just a suggestion.

justinfagnani commented 3 years ago

@tvvignesh

@customElement('my-element') can skip declaration if it is already in the registry instead of failing with an error.

We'd rather rely on a standard solution to this like Scoped CustomElementRegistry. Protecting against this exception can mask true errors in a program. We don't think that first-to-register is a reliable way to handle conflicts.

I also wouldn't want to make @customElement harder to explain. There's a big benefit to saying that it's just a declarative form of customElements.define() and behaves the same.

Re: VS Code plugins, I get CSS highlighting with lit-plugin: https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin I'm not sure what's happening for you, but I'd use that or file an issue if you already are.

Re: App recommendations. We're definitely looking into addressing these topics directly in docs. I'm not sure we'll call them recommendations or not, but we do want to guide developers towards answers for common needs like routing.

tsavo-vdb-knott commented 3 years ago

Hello all !

Some really great stuff going on in here - I wanted to post a snippet of some code which aims to give inspiration as to how we use controllers and lit-element within Runtime.dev today in our products - which is built on the component controller model as well as on top of LitElement. I also wanted to share some thoughts on the aforementioned topics!

Runtime.dev's component lib is built on a component controller model and seeks to achieve some of the things that are being noted here, Injectable styles, differential rendering, separation of state/logic and markup, both DOM and JS event dispatching & execution management, graph-linked/connected controllers, dynamic accessibility & focus management through controllers, etc. The list goes on.


protected description = {
        // Icon Controller Used in coordination with the input's validation state
        icon: new IconController({
            // Initial Icon SVGTemplate
            svg: CIRCLE_ICON,
            // Standardized rem sizing - can use IconSizeEnum.INHERIT to style externally 
            size: IconSizeEnum.SMALL,
            accessible: true, 
            aria: {
                label: 'Description Completed Icon',
                role: AriaRoleEnum.PRESENTATION
            }
        }),
        input: new InputController({
            // Differential Theme
            theme: DefaultTheme,
            // Initial States of input validation 
            valid: false,
            // Fully async Validation Function 
            validate: async ({ value }) => {
                const valid = Boolean(value);
                // Updating icon of other controllers when validated state changes 
                this.description.icon.svg = valid ? CHECK_ICON : CIRCLE_ICON;
                return {
                    valid,
                    error: {
                        message: '',
                        caught: false
                    }
                }
            },
            placeholder: 'A description also...',
            // Injectable templates that get injected into shadowRoot as opposed to slots 
            trailTemplateFn: ({ valid }) => html`<runtime-icon .controller=${this.description.icon} class=${valid ? 'valid' : 'invalid'}></runtime-icon>`,
            // Injectable Styles which apply to injected content
            styles: [css`
                runtime-icon.invalid { 
                    ${svgPrimaryColorLightest};
                }

                .valid { 
                    ${svgPrimaryColor};
                }
            `],
            //JS Tied in event callback
            engagedCb: async (e) => console.log(e)
        }),
    };

    get details() {
        const { information: { card, descriptor }, name, description, } = this;
        //Inject all controllers into markup
        //Components handle Animation, Interaction, Layout, and Styles
        return html`
            <runtime-card marginTop mdPadding .controller=${card}>
                <runtime-vertical-layout startAligned id="details">
                    <runtime-text .controller=${descriptor}></runtime-text>
                    <runtime-input marginTop .controller=${name.input}></runtime-input>
                    <runtime-input marginTop .controller=${description.input}></runtime-input>
                </runtime-vertical-layout>
            </runtime-card>
        `;
    };

Obviously Runtime.dev takes this model to an extreme and is several generations in but I think that the direction of LitElement/HTML 3.0 through a component-controller model is a great evolution.

A few things to note IMO:

Screen Shot 2020-09-17 at 2 34 18 PM

If you want to get a sense of of this direction and how it impacts WC applications at scale take a look at these:

Component Controller Model & LitElement

Component Controller Model At Scale Google Slides

Talk for Slides

Nevertheless - a bit of a shotgun thought process here due to controllers simply opening the floodgates on possibilities. Our lit-element WC have been controlled for about a year now and we have learned a lot. I would love to see a community perspective on this as it may impact how we move into the future and perhaps how we can help you avoid the madness we ran into here and there.

Would be happy to have a live chat/meet and go over a couple of these things/answer some questions. Runtime isn't open sourced yet - we are developing our public License right and scoping public demand for such a thing.

It is however actively being Licensed to our partners and we are under serval NDAs with Google, Microsoft, and others so I am available to guide/brainstorm pretty extensively without getting in trouble.

We are currently focused on building an upcoming product but we are always improving Runtime and we would love help bringing it into the community in conjunction with LitElement 3.0

I also read the PR's and Issues for LitElement and LitHTML daily and so I am very informed and happy to give my perspective on design choices being made and why we did or didn't do something similar.

Feel free to reach out personally at tsavo.knott@mesh-hq.com or @KnottTsavo on twitter.

Sorry for the typo's I'm on the run today!

Anyway - have a great day and happy coding!

All the best, -Tsavo

tvvignesh commented 3 years ago

@tvvignesh

@customElement('my-element') can skip declaration if it is already in the registry instead of failing with an error.

We'd rather rely on a standard solution to this like Scoped CustomElementRegistry. Protecting against this exception can mask true errors in a program. We don't think that first-to-register is a reliable way to handle conflicts.

I also wouldn't want to make @customElement harder to explain. There's a big benefit to saying that it's just a declarative form of customElements.define() and behaves the same.

Re: VS Code plugins, I get CSS highlighting with lit-plugin: https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin I'm not sure what's happening for you, but I'd use that or file an issue if you already are.

Re: App recommendations. We're definitely looking into addressing these topics directly in docs. I'm not sure we'll call them recommendations or not, but we do want to guide developers towards answers for common needs like routing.

@justinfagnani Thanks for your hint regarding the extension. Switching to the extension you had suggested worked great (able to get syntax highlighting and autocomplete for everything now 😃) I was using https://marketplace.visualstudio.com/items?itemName=bierner.lit-html before which has huge number of downloads.

Capture

Regarding scoped custom element registry, I read about the discussion you had initiated here: https://github.com/w3c/webcomponents/issues/716 and your gist here: https://gist.github.com/justinfagnani/d67d5a5175ec220e1f3768ec67a056bc . Looks like it has been 3 years since it started. Would it get implemented? 🤔 And even if it does at some point, I guess it would take ages for all the users to get the updates with all the browser vendors taking their own time to support it 😟 Anyways, I guess I have to go with my hack till then.

Btw, on a side note I remember this talk back when Polymer was the thing: https://www.youtube.com/watch?v=tNulrEbTQf8 Does Youtube still use Polymer or has it migrated to Lit? Any reference architecture we can get from there would be great. Currently I am sticking to the Open-wc recommendations but some real world prod reference would be great to have.

blikblum commented 3 years ago

Switching to the extension you had suggested worked great (able to get syntax highlighting and autocomplete for everything now 😃

I miss code collapse support: https://github.com/runem/lit-analyzer/issues/31

I really like LitElement but tooling support is way behind other libraries like React, Vue

tvvignesh commented 3 years ago

Switching to the extension you had suggested worked great (able to get syntax highlighting and autocomplete for everything now 😃

I miss code collapse support: runem/lit-analyzer#31

I really like LitElement but tooling support is way behind other libraries like React, Vue

Yup. I completely agree with you. Lit is great for the most part with all the features it currently has. So, instead of bloating it or changing things up a lot, its more important to put the investment to efforts like what Open-wc (Now modern-web) is doing, emphasising more on the performance and maybe create helpers, tooling and focus more on efforts towards things like this: https://github.com/material-components/material-components-web-components which can be a boon for not just people using Lit but also for people coming from other frameworks.

blikblum commented 3 years ago

@tvvignesh

I agree that Lit* (in fact web components) needs improves its ecosystem with e.g. more mature feature rich UI libraries / design systems but in my opinion performance is pretty good as is.

Developer experience should be priority (it's already good but things like missing code collapse in IDE gets in my way from time to time)

tvvignesh commented 3 years ago

@tvvignesh

I agree that Lit* (in fact web components) needs improves its ecosystem with e.g. more mature feature rich UI libraries / design systems but in my opinion performance is pretty good as is.

Developer experience should be priority (it's already good but things like missing code collapse in IDE gets in my way from time to time)

DId you try setting "editor.foldingStrategy": "indentation" as mentioned there. It worked for me.

blikblum commented 3 years ago

DId you try setting "editor.foldingStrategy": "indentation" as mentioned there. It worked for me.

No. See my comment there.

Thats just like i said above: from time to time issues in lit-* tooling requires me to think about it, look for workarounds etc. In a mature ecosystem this does not occurs

kwaclaw commented 3 years ago

@bengfarrell I think this might be nicely addressed with what I've been calling recently "reactive controllers" - helper objects that are able to hook and trigger the host element's lifecycle.

Imagine a controller called BoundsController which installs a ResizeObserver and on resize pulls the bounding rect and triggers an update.

Why not go all the way and also trigger life-cycle events from data change observations, like in MVVM? That could replace property change observations and allow for a pluggable way of triggering updates.

septatrix commented 3 years ago

I would also like to throw revisiting lit/lit-element#812 in the mix. The issue is about using ES2015 proxies to support reacting to changes to an object property or an element in an array. Currently such assignments are not recognized and need workarounds leading to confusion for some of my colleagues and errors.

Vue 3 is implementing the same feature and as shown by the last comment the implementation is fairly simple. As the next version of lit-html is not going to include IE11 support in the core library I think this is a good time to implement this.

busynest commented 3 years ago

Make custom elements compatible with native elements

justinfagnani commented 3 years ago

@septatrix lit-mobx already does a great job of that: https://github.com/adobe/lit-mobx It's very similar to Vue 3's reactivity library. There's a number of different way that LitElement authors are handling state change tracking, from MobX to Redux, to events + immutable data. I don't think we want to chose a particular way, but enable many of them. Being able to hook update() and call requestUpdate() are powerful primitives for these libraries to use.

justinfagnani commented 3 years ago

@busynest I'm not sure what you mean

kwaclaw commented 3 years ago

@justinfagnani With a proxy based observable approach one can replace a big part of lit-element's property tracking. A nice library to use for that is https://github.com/nx-js/observer-util. I have done exactly that with https://github.com/kwaclaw/KdSoft.lit-mvvm.

septatrix commented 3 years ago

I also see this as a superseeding of the current approach. Proxies allow a greater control and are able to react to more changes. Furthermore implementing this natively could be done in only a few lines and not add the weight of a comparatively huge dependency lige mobx, redux etc.

akc42 commented 3 years ago

My biggest problem was always that the I couldn't just develop with it because of the module specifiers. In the end the solution I had to use was to use rollup at npm install time to copy to a client/libs directory (most of my apps each has a common node_modules and separate server and client directories). I like that you are considering the out of the box ready to use - but please note that node_modules isn't always located at the root of the client directory.

Other than that lit-html, lit-element and webcomponents loader is all I've needed (other than my own utilities) (I don't build, my production systems don't seem to need it - not even minified anything).

kwaclaw commented 3 years ago

@akc42 I am using Snowpack for the same purpose.

a11delavar commented 3 years ago

Nice work, excited about "Declarative Events"!

ghost commented 3 years ago

Be able to pass an @query to another child element.

Currently if I need to pass a sibling to another child element I would do something like this:

@query('foo-element') foo!:FooElement;

render() {
  return html`
    <foo-element></foo-element>
    <other-element .foo=${this.foo}></other-element>
  `;
}

However that will not work on first render since isn't in the dom yet and the query will fail. So currently you have to work around this by waiting for first update and then querying and then manually passing to the other child. It would be great if there was some mechanism that the property could automatically be resolved without rendering twice or reverting to some imperative programming.

bennypowers commented 3 years ago

Typed PropertyValues would be really clutch. For static properties getter, I'm sure it could be inferred. not sure about decorators, though.

@property() newPropName: string;

updated(changed: PropertyValues) {
  if (changed.has('oldPropName')) 
    whoops();
}
cdata commented 3 years ago

I would love to see a considered answer to the question, "what does it mean to render without a shadow root?"

In practice, one does not always need to create a shadow root in order to have a useful component implementation. There are a handful of cases today where it would be better not to use a shadow root.

LitElement as of 2.x makes rigid assumptions (as a matter of internal implementation) regarding the usage of a shadow root. This suggests to users that although it may be possible to create components without a shadow root, it is not the intended usage of LitElement. This in turn interrupts the exploration and adoption of novel, higher-order usages of LitElement.

It is not necessary for LitElement to establish a first-class workflow for components that do not use shadow roots. However, the LitElement base class should become a viable candidate for such a workflow, one that does not levy a cognitive or performance tax on authors who seek to use it that way.

web-padawan commented 3 years ago

Idea for lit-labs package: consider implementing a portal directive which could serve for building modals, tooltips etc.

Right now pretty much every more or less complex web components library has its own mechanism. At Vaadin, we use vaadin-overlay which teleports elements under document body. Other alternatives include scanning DOM tree to manage z-index.

It would be nice to have a recommended approach. There used to be iron-overlay prototype under PolymerLabs but it hasn't been finished.

Poopooracoocoo commented 3 years ago

https://github.com/Polymer/lit-element/issues/1077#issuecomment-694749771

Does Youtube still use Polymer or has it migrated to Lit?

(totally off topic lol) Yikes. YouTube still uses Polymer?! No wonder its so slow /s. I had been using an extension to use the old YouTube until YouTube broke it earlier this year. :((( um. yay opt-in polyfills. ie needs to die.

JosefJezek commented 3 years ago

Any solution to solve unistore binding to LitElement.

My not ideal solution... I would like to declare dynamically properties for the state props. I must declare observedState object now.

export const connect = store => superclass =>
  class extends superclass {
    static get properties() {
      return {
        // The observedState contains selected the store state props by observedStateProps var.
        // Any change of observedState will be run render() of LitElement.
        observedState: { type: Object },
      };
    }

    constructor() {
      super();
      this.state = {};
      this.oldState = {};
      this.observedState = {};
      this.observedStateProps = [];
      this._stateChanged = this._stateChanged.bind(this);
    }

    connectedCallback() {
      if (super.connectedCallback) super.connectedCallback();

      store.subscribe(this._stateChanged);
      this._stateChanged(store.getState());
    }

    disconnectedCallback() {
      store.unsubscribe(this._stateChanged);

      if (super.disconnectedCallback) super.disconnectedCallback();
    }

    // The `_stateChanged(state)` method will be called when the state is updated.
    _stateChanged(state) {
      const observedState = {};

      this.observedStateProps.forEach(prop => {
        observedState[prop] = state[prop];
      });

      this.oldState = this.state;
      this.state = state;

      if (
        Object.keys(observedState).length &&
        !equals(this.observedState, observedState)
      )
        this.observedState = observedState;

      this.stateChanged(state);
    }

    // eslint-disable-next-line class-methods-use-this
    stateChanged() {}
  };

Inspired by

cdata commented 3 years ago

(apologies, as this is somewhat off-topic)

@JosefJezek I have experimented with Unistore-like patterns on top of LitElement. I have a very radioactive / not-well-documented repo with my experiments here: https://github.com/cdata/bag-of-tricks

Usage ends up looking like this:

// A store:

const store = new Store<FooState>({
  bar: 'baz'
});

// An action creator that operates on the store:

const setBar = (bar: string): Action<FooState> => (getState, dispatch) => {
  return {
    ...getState(),
    bar
  };
};

// A connected component:

@customElement('foo-component')
export class FooComponent extends connect<FooState>()(LitElement) {
  @property({ type: String })
  @selector<FooState>((state) => state.bar)
  bar: string?;

  #setBar = selfDispatch(setBar);

  render() {
    return html`
<button @click="${() => this.#setBar('zot')}">Set bar to zot</button>
    `;
  }
}

// Meanwhile, in some entrypoint:

provide(self, store);

Some details about the above example:

The thing I like about this approach is that it neatly decouples the store from the component, which makes testing a lot simpler. In the course of a test, we can trivially substitute different stores without changing the implementation of a component (or trying to come up with some kind of fancy module substitution for its dependencies).

Anyway, feel free to borrow any of those ideas. I would be happy to talk about it more in an issue thread in that repo, if it interests you.

jperera84 commented 3 years ago

@justinfagnani Great things are being proposed here, I only wanted to ask you to keep LitElement free from to much "sugar code" and keep LitElement standards, I personally choose LitElement because is near to the platform and it doesn't look like Angular or React or any other Framework/Library...