Giter VIP home page Giter VIP logo

fable.lit's Introduction

Fable.Lit

Fable.Lit is a collection of tools to help you write Fable apps by embedding HTML code into your F# code with the power of Lit.

Before doing anything make sure to install the dependencies after cloning the repository by running:

npm install

How to test locally ?

npm run test

How to publish a new version of the package ?

npm run publish

How to work on the documentation ?

  1. npm run docs -- watch
  2. Go to http://localhost:8080/

How to update the documentation ?

Deployment should be done automatically when pushing to main branch.

If the CI is broken, you can manually deploy it by running npm run docs:deploy.

fable.lit's People

Contributors

alfonsogarciacaro avatar angelmunoz avatar billhally avatar jordanmarr avatar leolorenzoluis avatar mangelmaxime avatar marcpiechura avatar pkese avatar

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fable.lit's Issues

Hooks.useTransition is leaving transition-enter classname to its next sibling.

So I implemented it and it almost is identical to the animations that you have for TodoMVC. However, when I try to delete an item from the list say in the middle, then the next item will add transition-enter on the next sibling. It looks like transition-enter is getting applied to the next item after transition.triggerLeave is called?

Given the following code:

[<HookComponent>]
let Monitor (item: Monitor) dispatch =
    let transitionMs = 200
    let transition = Hook.useTransition(transitionMs, onLeft = (fun () -> DeleteMonitor item.Id |> dispatch))
    let className = Hook.use_scoped_css $"""
        :host {{
            transition-duration: {transitionMs}ms;
            border: 2px solid lightgray;
            border-radius: 10px;
            margin: 5px 0;
        }}
        :host.transition-enter {{
            opacity: 0;
            transform: scale(2);
        }}
        :host.transition-leave {{
            opacity: 0;
            transform: scale(0.1);
        }}
        .is-clickable {{
            user-select: none;
        }}
    """
    html $"""
    <div class="{className} {transition.className}" {LitLabs.motion.animate()}>
        <div>
            <p>{item.Id} {item.FriendlyName} {item.URL}</p>
            <sl-button @click={Ev (fun _ -> transition.triggerLeave())}>Delete monitor</sl-button>
            <sl-button @click={Ev (fun _ -> EditMonitor item |> dispatch)}>Edit monitor</sl-button>
        </div>
    </div>
    """

[<HookComponent>]
let Page() =
    let model, dispatch = Hook.useElmish(init, update)
    let renderItem (item: Monitor) =
        match model.Edit with
        | Some value when value.Id = item.Id->
            html $"""
            <div>
                <p>{item.Id}</p>
                <input value={item.FriendlyName} @keyup={EvVal (fun e -> UpdateFriendlyName e |> dispatch)}/>
                <input value={item.URL}/>
                <sl-button @click={Ev (fun e -> SaveMonitor model.Edit.Value |> dispatch)}>Save monitor</sl-button>
            </div>
            """
        | _ ->
            Monitor item dispatch
       
    html $"""
     <sl-button @click={Ev (fun e ->
            let newMonitor = {
                Id = Random.Shared.Next() 
                FriendlyName = "Test1"
                URL = "2"
                Retries = 100
                HeartBeatInterval = 100
            }
            CreateMonitor newMonitor |> dispatch)}>New monitor</sl-button>
        {model.Monitors |> Seq.map renderItem}
    """

Screen Shot 2022-07-05 at 12 14 46 AM

When the delete button is clicked, the next sibling is left hanging with transition-enter. I expect no transition class name is left hanging. I've checked the update function that I have and the delete is only getting called once.

Update: I changed model.Monitors |> Seq.map to Lit.mapUnique and it works perfectly fine. Documentation refer to the method as

mapUnique
You can pass any iterable (including F# lists) to Lit, but when it's important to identify items in a list (e.g. when the list can be sorted, filtered or included item transitions), use Lit.mapUnique to give each item a unique id.

Not sure what included item transition means here and why would it fix it? Also, why does mapUnique expects the Id to be a string?

early observations

I've been having fun with this, I ended up with this sample repo

https://github.com/AngelMunoz/Flit

My only observation would be that it seems (due how to Lit works) if you create a standalone function that has internal state will not be rendered because render is only called when a change in the elmish state, my solution was to call each "page" which in turn fires it's own elmish loop, I'm not sure if that's a good way to do it

this might make things cumbersome in the long run since anything that requires internal state kind of requires an elmish loop so far

I'm still experimenting with it and if I come across more things I'll share them

[Feliz.Lit] Uncaught TypeError: Cannot read property '0' of undefined

When using Two hook components with Feliz.Lit like the following

Fable CLI: 3.3.0-beta-002

[<HookComponent>]
let counter (props: {| initial: int option |}) =
    let count, setCount =
        Hook.useState (props.initial |> Option.defaultValue 0)

    Html.div [
        Html.p $"Home: {count}"
        Html.button [
            Ev.onClick (fun _ -> setCount (count + 1))
            Html.text "Increment"
        ]
        Html.button [
            Ev.onClick (fun _ -> setCount (count - 1))
            Html.text "Decrement"
        ]
        Html.button [
            Ev.onClick (fun _ -> setCount (0))
            Html.text "Reset"
        ]
    ]


[<HookComponent>]
let app () =
    Html.article [
        Html.main [
            Html.section [
                Html.h1 "Functions with parameters and state!"
                counter ({| initial = None |}) 
                counter ({| initial = Some 100 |})
            ]
        ]
    ]
    |> Feliz.toLit


LitHtml.render (app (), document.querySelector ("#app"))

causes an error like this

http://localhost:8080/dist/.fable/Fable.Lit.Feliz.1.0.0-beta-003/Lit.Feliz.fs.js [:97:578]
TypeError: Cannot read property '0' of undefined
    at http://localhost:8080/dist/.fable/Fable.Lit.Feliz.1.0.0-beta-003/Lit.Feliz.fs.js:97:578
    at uncurriedFn (http://localhost:8080/dist/.fable/fable-library.3.3.0-beta-002/Util.js:520:44)
    at fold (http://localhost:8080/dist/.fable/fable-library.3.3.0-beta-002/List.js:405:15)
    at http://localhost:8080/dist/.fable/Fable.Lit.Feliz.1.0.0-beta-003/Lit.Feliz.fs.js:84:36
    at http://localhost:8080/dist/.fable/Fable.Lit.Feliz.1.0.0-beta-003/Lit.Feliz.fs.js:97:347
    at uncurriedFn (http://localhost:8080/dist/.fable/fable-library.3.3.0-beta-002/Util.js:520:44)
    at fold (http://localhost:8080/dist/.fable/fable-library.3.3.0-beta-002/List.js:405:15)
    at http://localhost:8080/dist/.fable/Fable.Lit.Feliz.1.0.0-beta-003/Lit.Feliz.fs.js:84:36
    at http://localhost:8080/dist/.fable/Fable.Lit.Feliz.1.0.0-beta-003/Lit.Feliz.fs.js:97:347
    at uncurriedFn (http://localhost:8080/dist/.fable/fable-library.3.3.0-beta-002/Util.js:520:44)

It gets fixed if you change these lines

// at the end of counter
|> Feliz.toLit
// inside

[<HookComponent>]
let app () =
    Html.article [
        Html.main [
            Html.section [
                Html.h1 "Functions with parameters and state!"
                counter ({| initial = None |}) |> Feliz.ofLit
                counter ({| initial = Some 100 |}) |> Feliz.ofLit
            ]
        ]
    ]
    |> Feliz.toLit

but if you try the following doesn't work

counter ({| initial = None |}) |> Feliz.toLit |> Feliz.ofLit

What does this mean?

Given the following code that is just rendering a list of items

let value, setValue = Hook.useState item
let inputRef = Hook.useRef<HTMLInputElement>()

html $"""
<div>
    <input value={value.Id} />
    <input type="text" {Lit.refValue inputRef} value={value.FriendlyName} @keyup={EvVal (fun e -> printfn "Hello %A" e; setValue { value with FriendlyName = e })}/>
    <input value={value.URL}/>
    <sl-button @click={Ev (fun e -> SaveMonitor value |> dispatch)}>Save monitor</sl-button>
</div>
"""

Error: Hooks must be called consistently for each render call
at HookContext.fail (Hook.fs:95:9)
at HookContext.checkRendering (Hook.fs:126:32)
at HookContext.useState (Hook.fs:171:9)

[Feliz.Lit] Static templates in lit-html 2.0

I tried the Fable.Lit and at the beginning they seemed to be working but then I realized the whole DOM was updated for every render. Some debugging revealed the reason is in lit-html 2.0 (currently rc-004) they're using a WeakMap to identify templates already rendered. I think I've managed to fix this in Fable by compiling to JS template strings (even if I've to wrap them again to conform to FormattableString API), but with Feliz.Lit.toLit this is not possible because templates are generated on-the-fly.

A workaround is tricky. Even if we could keep a "static" reference (but that can be garbage collected) per toLit call and have our own WeakMap, we cannot be sure the template will be the same every time because Feliz code is dynamic. The only solutions I can think of atm are:

  • use a lit-html directive to render to mount directly on the DOM, but then we don't have the diffing unless we import a virtual dom like Snabbdom or a similar technique
  • focus on interaction with React so we can use "real" Feliz
  • try to "pre-compile" Feliz.Lit templates somehow
  • just forget about converting Feliz.Lit code to Lit (except maybe for the styles).

What do you think @AngelMunoz?

Simple SVG is not rendering properly

I am not sure if this is Lit related or just Fable, but given the following code:

let createBar index value =
    let ratio = 240 / 10
    html $"""
            <rect 
              fill="red"
              x={( 320 / 10 )  * index}
              y={240 - ( value * ratio )}
              width={320 / 10}
              height="{value * ratio}"/>
        """

let chart () =
    html $"""
    <svg width="320" height="240">
      {
       seq { 1..11 } |> Seq.mapi(createBar)
      }
    </svg>
    """

It seems that the <rect> is not being rendered properly. I can't seem to find any difference between the text, but when I try to edit the HTML and just remove either the space in between <rect> or something then the rect renders.

<svg width="320" height="240">
    <!--?lit$646399511$--><!---->
    <rect fill="red" x="0" y="216" width="32" height="24">
    </rect><!----><!---->
    <rect fill="red" x="32" y="192" width="32" height="48">
    </rect><!----><!---->
    <rect fill="red" x="64" y="168" width="32" height="72">
    </rect><!----><!---->
    <rect fill="red" x="96" y="144" width="32" height="96">
    </rect><!----><!---->
    <rect fill="red" x="128" y="120" width="32" height="120">
    </rect><!----><!---->
    <rect fill="red" x="160" y="96" width="32" height="144">
    </rect><!----><!---->
    <rect fill="red" x="192" y="72" width="32" height="168">
    </rect><!----><!---->
    <rect fill="red" x="224" y="48" width="32" height="192">
    </rect><!----><!---->
    <rect fill="red" x="256" y="24" width="32" height="216">
    </rect><!----><!---->
    <rect fill="red" x="288" y="0" width="32" height="240">
    </rect><!----><!---->
    <rect fill="red" x="320" y="-24" width="32" height="264">
    </rect><!---->
    </svg>

It's being called from a HookComponent

[<HookComponent>]
let Page() =
       html $"""{chart()}"""

I tried changing Seq.mapi to Lit.mapUnique but that didn't make a difference.

Also when I try to set a breakpoint on chart function then the function is called twice? Is that expected? Is it due to some lifecycle?

Add renderRoot in host (LitElement)

Hello,

I think we should had renderRoot in type LitElement.

I try to do something like this

let input () : HTMLInputElement =
        host?renderRoot?querySelector ("#newitem")

Maybe I miss something with host.el ???
when I change useShadowDom to false this is working.

What do you think ?

Problem importing raw css into ShadowDom

Importing verbatim css from an external source into a LitConfig.styles element does not work:

let mycss' : {| ``default``: string |} = JsInterop.importAll "./public/some.css"
let mycss = mycss'.``default``
...
LitElement.init (fun cfg ->
  cfg.useShadowDom <- true
  cfg.styles <- [ css $"{mycss}" ]
)

This compiles just fine, but crashes at runtime:

Error: LitElement.init must be called on top of the render function
    LitElementUtil_failInit http://localhost:3000/build/client/fable_modules/Fable.Lit.1.4.1/LitElement.fs.js:24
    Decorate http://localhost:3000/build/client/fable_modules/Fable.Lit.1.4.1/LitElement.fs.js:301
    <anonymous> http://localhost:3000/build/client/OlMap.js?import&t=1653653843482:180
[client.ts:28:12](http://localhost:3000/src/client/client.ts)

As a workaround, this works:

[<Import("unsafeCSS", "lit")>]
let unsafeCSS str = jsNative

LitElement.init (fun cfg ->
  cfg.useShadowDom <- true
  cfg.styles <- [ unsafeCSS mycss ]
)

Otherwise Fable.Lit is awesome!

Upgrading to Fable.Lit 1.4.2 causes issues in Elmish types

I am testing with the Fable.LitRouter and LitState but it requires 1.4.2, I get compilation error if I get 1.4.2 but if I downgrade to 1.4.1 then there's no error. This is just showing in Jetbrain's Rider. If I compile with npm run start then there's no error.

Looking at the commit history/release notes there shouldn't be anything that should affect this in 1.4.2.

Screen Shot 2022-09-27 at 5 53 32 PM

Screen Shot 2022-09-27 at 5 56 47 PM

Looking at the release notes there shouldn't be any impact on the Elmish/Lit libs.

For reference this is the sample code that I tried and updating new variables to make it minimal


let initialState1() =
    let monitorPageModel, monitorPageMsg = ListMonitorsPage.init()
    let welcomePageModel, welcomePageMsg = WelcomePage.init()
    
    let model = { CurrentPage = Anonymous AnonymousPage.Welcome; ServerMessage = []; MonitorModel =  monitorPageModel; WelcomePageModel = welcomePageModel }
    model, Cmd.none
    
let update1 msg model =
    model, Cmd.none

let view1 model dispatch =
    html $"""
    <div>Hello world</div>
    """


Program.mkProgram initialState update view
|> Program.withLit "my-app"
|> Program.withBridgeConfig (Bridge.endpoint socketEndpoint |> Bridge.withMapping ServerMsg)
|> Program.run

Screen Shot 2022-09-27 at 5 57 51 PM

Update:

Looks like something in these two packages are causing the compiler to get confused.

<PackageReference Include="Fable.LitRouter" Version="1.0.0-beta-005" />
<PackageReference Include="Fable.LitStore" Version="1.0.0-beta-001" />

If I remove that and just keep Lit to 1.4.2 it has no compile error

I've narrowed it down. It's the Fable.LitRouter that's causing an issue. Not sure where the source code for LitRouter is?

I extracted the library and I don't get any errors, so not sure why this is happening?

I removed the Fable.LitRouter and just created a separate fs file from https://nuget.info/packages/Fable.LitRouter/1.0.0-beta-005 Maybe @JordanMarr knows?

Should we delete the "custom HMR" ?

Hello @alfonsogarciacaro,

someone had an issue with how the namespaces/modules are organized in Fable.Lit.Elmish (see elmish/hmr#33).

His issue, also made me think about the fact that this is confusing to have an HMR implementation in this package and another one advertise "more global" to Elmish in Fable.Elmish.HMR.

Now that Fable.Elmish.HMR supports Parcel, Webpack and Vite (elmish/hmr#29) what do you think of removing the copy/paste code from this repository?

From my quick tests it seems like Fable.Elmish.HMR works with this package too as this is just an Elmish application in the end?

If we are to do that, I suppose we need split Fable.Elmish.HMR into smaller modules to avoid having Fable.React being introduced and so femto asking to ask to install react and react-dom.

Class Based API vnext

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

hasChanged and currying problems

I am trying to use hasChanged

when I give a function as in below

hasChanged = fun x y -> printf "%A" (x,y); false

it never runs

When I do below

hasChanged = fun(x, y) -> printf "%A" (x,y); false

It fires but tuplizes the first argument and second arg is missing.

Func<_,_,_>``` trick won't help either. Don't fire.

Q: wrapping requirejs amd module (durandaljs interop)

is there a way to wrap within a web-component a requirejs/amd module?

i am using a very old durandaljs (then aureliajs) app, and it uses requirejs and amd module loading.
https://github.com/BlueSpire/Durandal
https://www.infoq.com/articles/durandal-javascript-framework/

i was thinking i could turn that to interop with F# and Fable.Lit and progressively migrate away from it completely

index.html

<html>
    <head>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" data-main="app/main"></script>
    </head>
    <body>
        <div id="applicationHost"></div>
    </body>
</html>

Main.js

requirejs.config({
    paths: {
        'text': 'https://cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text.min',
        'durandal':'https://cdn.jsdelivr.net/npm/[email protected]/js',
        'plugins' : 'https://cdn.jsdelivr.net/npm/[email protected]/js/plugins',
        'transitions' : 'https://cdn.jsdelivr.net/npm/[email protected]/js/transitions',
        'knockout': 'http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.5.0',
        'jquery': 'https://ajax.googleapis.com/ajax/libs/jquery/2.2.1/jquery.min',
        } 
    });
    
define(function (require) {
    var system = require('durandal/system');
    var app = require('durandal/app');

    system.debug(true);

    app.title = 'Durandal Starter Kit';

    app.configurePlugins({
        router:true,
        dialog: true
    });

    app.start().then(function() {
        app.setRoot('shell');
    });
});

and the main view: Shell.js

define(function (require) {
    var app = require('durandal/app');
    var ko = require('knockout');
    return {
        name: ko.observable(),
        sayHello: function () {
            app.showMessage('Hello ' + this.name() + '! Nice to meet you.', 'Greetings');
        }
    };
});

And Shell.html.. they use Knockout.js for 2-ways bindings...
https://knockoutjs.com/index.html

was just curious which approach would you go to use Fable.Lit on this one, if any idea

thank you so much, i am quite new to js for now i have managed to transpile to ts and back to js but took me a while, i was checking also fable alternatives as F# is my fav lang :)

Incongruence in documentation

the namespace/module Lit.Elmish.HMR shown in this example doesn't seem to exist

open Elmish
open Lit

type Model = ..
type Msg = ..

let init() = ..
let update msg model = ..
let view model dispatch = ..

open Lit.Elmish
open Lit.Elmish.HMR

Program.mkProgram initialState update view
|> Program.withLit "app-container"
|> Program.run

[Lit]: error FABLE: Unexpected base call expression, please report

When compiling on a fresh project the first time I'm getting this error

C:/x/Fable.Haunted/src/Fable.Haunted.Store/.fable/Fable.Lit.1.0.0-beta-002/Hook.fs(1,1): error FABLE: Unexpected base call expression, please report
Compilation failed

the error goes away when you try to compile again

Dispatch events

I think we're missing an integral part of Lit I don't remember adding (and it flew over my head sorry) anythign related to dispatching events from the LitElement

class MyEventSource extends LitElement {

  raiseEvent() {
    const evt = new Event('my-event', { bubbles: true, cancelable: true, composed: true });
    this.dispatchEvent(evt)
  }

  render() {
     html`
       <button @click=${this.raiseEvent}>I will send an Event</button>
     `;
  }
}

in F# we would need to do something like this

[<Emit("new Event($0, $1)")>]
let createEvent name opts = jsNative

[<LitElement("my-event-source")>]
let private MyEventSource () =
  LitElement.init()
  let raiseEvent ev =
    let evt = createEvent "go-back" {| bubbles = true; composed = true; cancelable = true |}
    (ev.target :?> HTMLElement).dispatchEvent evt

  html
    $"""
       <button @click={raiseEvent}>I will send an Event</button>
  """

Which I guess it's fine, but the target of the event won't be the web component itself (as in Lit) but the button that is dispatching the event

I think if we add a helper function in the LitElement class defined here

type LitElement() = 
   (* ... other members ... *)
   static member inline dispatchEvent (event: Event) =
       // I'm not sure if this is really the HTMLElement but my intuition tells me so
       jsThis<HTMLElement>.dispatchEvent(event)

could fix the dispatch target.

[<Emit("new Event($0, $1)")>]
let createEvent name opts = jsNative

[<LitElement("my-event-source")>]
let private MyEventSource () =
  LitElement.init()
  let raiseEvent ev =
    let evt = createEvent "go-back" {| bubbles = true; composed = true; cancelable = true |}
    LitElement.dispatchEvent evt

  html
    $"""
       <button @click={raiseEvent}>I will send an Event</button>
  """

The creation of events can be worked in the fable-browser repo because I think we've (mostly I, plead guilty) been recently adding helpers in many of the samples and I think that should belong there to be honest ๐Ÿ˜€

Reference: https://lit.dev/docs/components/events/#dispatching-events

Hook.useTransition with more events

I'm not sure if it's missing, but would it be useful to add additional event handlers on the Hook.useTransition

Currently it uses OnEntered and OnLeft.

Adding such as OnEntered, OnEntering... etc

Also, would it be more useful if the OnLeft was renamed to Exit instead to be on on par with other naming conventions by other frameworks?

What I'm trying to do is assign TailWindCss on different transitions. I'm not sure if there's a better way to do it

Maybe something like https://github.com/reactjs/react-transition-group/blob/2989b5b87b4b4d1001f21c8efa503049ffb4fe8d/stories/Transition.js#L13

Writable attributes

I am trying to create a custom web component that takes attributes:

[<LitElement("fluent-icon")>]
let FluentIcon() = 
    let _, props =
        LitElement.init(fun init ->
            init.useShadowDom <- false
            init.props <- 
                {| 
                    name = Prop.Of(defaultValue = "Add", attribute = "name")
                    size = Prop.Of(defaultValue = "20px", attribute = "size")
                    color = Prop.Of(defaultValue = "red", attribute = "color")
                |}
        )
    let name = props.name.Value
    let color = props.color.Value
    let size = props.size.Value
    html $"""<i class="ms-Icon ms-Icon--{name}" style="color: {color}; font-size: {size};" aria-hidden="true"></i>"""

Usage:

<fluent-icon name="Add" color="Red" size="20px"></fluent-icon>

It works if I use static html with no inputs, but adding the the 3 input attributes: init.props <- {| ... |} results in the following error:

LitElement.fs:275 
        
       Uncaught TypeError: Cannot set property name of #<classExpr> which has only a getter
    at LitElement.fs:275:35
    at Seq.js:837:9
    at fold (Seq.js:691:19)
    at iterate (Seq.js:836:5)
    at initProps (LitElement.fs:274:40)
    at new LitHookElement$1 (LitElement.fs:185:8)
    at new classExpr (LitElement.fs:288:29)
    at TemplateInstance._clone (lit-html.ts:927:52)
    at ChildPart._commitTemplateResult (lit-html.ts:1270:33)
    at ChildPart._$setValue (lit-html.ts:1165:12)

Accessing children in custom element

I know is more of a discussion than an issue, but for lack of a discussion forum I will post it here:
Is it possible to access an array of children elements in my custom element?

[Feliz.Lit] Set Properties

(this may be better in Feliz.Engine, feel free to move it there if necessary)

Currently with the Feliz flavor you can only set attributes via Attr.custom("my-attribute", "it's value") but unless I'm lost (very likely) there are not ways to set properties of an element with the template strings flavor you can do

let myValue = "some value"
let myObj = {| value = 10 |}
<some-element my-attribute={myValue} .myProperty={myObj}></some-element>

it would be ideal to have this on the Feliz flavor as well

Self closing tag LitComponents are not closing properly if there's an HTML element after it

Given the following code:

let createBar index value =
    printfn "%A %A" index value
    let ratio = 240 / 10
    html $"""
            <rect 
              fill="red"
              x={( 320 / 10 )  * index}
              y={240 - ( value * ratio )}
              width={320 / 10}
              height="{value * ratio}"/>
        """

[<LitElement("ping-bar")>]
let chart () =
    
    // This call is obligatory to initialize the web component
    let _, props =
        LitElement.init(fun init ->
            init.props <- {| initial = Prop.Of(defaultValue = 0) |})
    html $"""
    <svg width="320" height="240">
      {
       seq { 1..11 } |> Seq.mapi(createBar)
      }
      {(createBar 5 6)}
      <rect fill="red" x="240" y="97" width="32" height="264"/>
    </svg>
    """

[<HookComponent>]
let Page() =
    let model, dispatch = Hook.useElmish(init, update)
    let renderItem (item: Monitor) =
        match model.Edit with
        | Some value when value.Id = item.Id->
            html $"""
            <div>
                <p>{item.Id}</p>
                <input value={item.FriendlyName} @keyup={EvVal (fun e -> UpdateFriendlyName e |> dispatch)}/>
                <input value={item.URL}/>
                <sl-button @click={Ev (fun e -> SaveMonitor model.Edit.Value |> dispatch)}>Save monitor</sl-button>
            </div>
            """
        | _ ->
            Monitor item dispatch
       
    html $"""
     <sl-button @click={Ev (fun e ->
            let newMonitor = {
                Id = Random().Next()
                FriendlyName = "Test1"
                URL = "2"
                Retries = 100
                HeartBeatInterval = 100
            }
            CreateMonitor newMonitor |> dispatch)}>New monitor</sl-button>
        <ping-bar/>
        {model.Monitors |> Lit.mapUnique (fun t -> t.Id.ToString()) renderItem}
    """

The {model.Monitors |> Lit.mapUnique (fun t -> t.Id.ToString()) renderItem} is getting rendered inside the <ping-bar> instead of closing the tag on itself.

Solution: Changing <ping-bar/> to <ping-bar></ping-bar> fixes it.

Lit Controllers don't work with Fable.Lit WC

If you add a "native" Lit component and a controller inside a Fable.Lit application they work just fine:
https://github.com/AngelMunoz/ControllersRepro/blob/master/src/Controllers/controllers.js
https://github.com/AngelMunoz/ControllersRepro/blob/master/src/Components/Navbar.fs#L51

but if you want to add the same controller to a Fable.Lit Web component, every time a render happens the controller gets added time and time again to the controller's list
I made a reproduction repository to check it out
https://github.com/AngelMunoz/ControllersRepro

Although we may not author controllers from Fable.Lit there will be some packages in the wild that may be useful to consume

Lit.mapiUnique

I think it's useful to create a helper function similar to mapi in F#. I'd like to access the index to be passed to the function besides the value of an array.

LitLabs.motion.animate() throws an error

The prototype that I have was working when Lit root is using non Elmish (basically similar to switching the two here) https://github.com/fable-compiler/Fable.Lit/blob/main/sample/App.fs#L47

Now when I load a LitComponent with Elmish I get the following error on the console:

animate.ts:195 Uncaught TypeError: Cannot read properties of undefined (reading 'addController')
    at Animate.update (animate.ts:195:18)
    at Animate._$resolve (directive.ts:134:17)
    at resolveDirective (lit-html.ts:1120:24)
    at ElementPart._$setValue (lit-html.ts:2077:5)
    at TemplateInstance._update (lit-html.ts:1224:16)
    at ChildPart._commitTemplateResult (lit-html.ts:1566:16)
    at ChildPart._$setValue (lit-html.ts:1418:12)
    at classExpr.setValue (async-directive.ts:366:19)
    at classExpr.requestUpdate (Hook.fs:338:9)
    at HookContext.setState (Hook.fs:148:17)

I had the following TemplateResult

    html $"""
    <div class="{className} {transition.className}" {LitLabs.motion.animate()}>
        <div>
            <p>{item.Id} {item.FriendlyName} {item.URL}</p>
            <sl-button @click={Ev (fun _ -> transition.triggerLeave())}>Delete monitor</sl-button>
            <sl-button @click={Ev (fun _ -> EditMonitor item |> dispatch)}>Edit monitor</sl-button>
        </div>
    </div>
    """

Removing the {LitLabs.motion.animate()} removes the error and it still animates, but not sure if I might have hide done something I'm not aware of.

How to import FluentUI web components

Greetings!

I'm trying out Fable.Lit and trying to import and register the FluentUI web components, but I'm not sure how to do it.

I tried JsInterop.importAll "@fluentui/web-components", but according to the install guide it looks like I need to call:

import { allComponents, provideFluentDesignSystem } from '@fluentui/web-components';
provideFluentDesignSystem().register(allComponents);

So I guess I need to create some minimal custom bindings to accomplish this?

Use of Hook.useRef

In https://fable.io/Fable.Lit/docs/hook-components.html the first example shows the use of Hook.useRef:

open Lit

[<HookComponent>]
let NameInput() =
    let value, setValue = Hook.useState "World"
    let inputRef = Hook.useRef<HTMLInputElement>()

    html $"""
      <div class="content">
        <p>Hello {value}!</p>
        <input {Ref inputRef}
          value={value}
          @keyup={EvVal setValue}
          @focus={Ev(fun _ -> inputRef.Value |> Option.iter (fun el -> el.select()))}>
      </div>
    """

1 - In the row '<input {Ref inputRef}', where is the symbol 'Ref' defined? It doesn't seem to exist
2 - Is there a complete example/demo where we can see how useRef is intended to be used?

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.