fable-compiler / Fable.Lit

Write Fable Elmish apps with Lit
https://fable.io/Fable.Lit/
MIT License
91 stars 13 forks source link

Class Based API vnext #40

Open AngelMunoz opened 2 years ago

AngelMunoz commented 2 years ago

Taking advantage of the reactive controllers branch and it's very likely major version bump I also wanted to follow up #36 where it would be nice to expose the class based API to have better bind parity to lit.dev and also to allow newer users who may come from JS themselvs (or even other backgrounds) to be easier to just follow lit's docs and be able to do it on F# as well.

One of the big issues is that we can't emit typescript-like decorators yet and we shouldn't because the HTML spec differs from typescript's implementation (I'm not sure up to what detail but I know it does)

that being said I'm working on a computation expression which kind of matches the customElement decorator from typescript we still need to track property definition vs properties inside the class but in my mind it should not be too much of a hassle because it's the same behavior lit.dev has in JavaScript

The proposed API would look like this

// classic class component definition for lit components
type UserProfile() =
    inherit LitElement()
    // manual declaration of internal properties
    let mutable name = ""
    let mutable age = 0

    override _.render() =
        html $"<p>{name} - {age}</p>"

// this auto registers the element using the same
// behavior as the decorator would do
registerElement "user-profile" jsConstructor<UserProfile> {
    property "name" {
        use_attribute
        use_type StringCtor
    }
    property "age" {
        use_attribute
        use_type JS.Constructors.Number
    }
}

It's usage would be as usual

<user-profile name="Peter" age="25"></user-profile

What it brings to the table is the ability to build the properties in a kind of type safe way with some auto-completition vs building the properties manually and try to remember each possibility

{| name = {| ``type`` = StringCtor; attribute = true |}
   age = {| ``type`` = JS.Constructors.Number; attribute = true |} |}

For cases where one may want to provide a component library for other F# users I also thought to have a way to let users cherry pick their own elements

[<AutoOpen>]
module ElementDefinitions = 
  let userProfileDef =
    delayedElement "user-profile" jsConstructor<UserProfile> {
      property "name" {
        use_attribute
        use_type StringCtor
      }
      property "age" {
        use_attribute
        use_type JS.Constructors.Number
      }
    } // ElementDefinition rather than auto registration

// let the user choose what they want to actually use
module CherryPick = 
  let userProfile() =
    // defineElement will also be provided by us
    // (likely the same function used in the registerElement CE's Run method)
    defineElement userProfileDef
AngelMunoz commented 2 years ago

current progress on this area is on #41