-
Notifications
You must be signed in to change notification settings - Fork 13
Experimental class bindings #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: controllers
Are you sure you want to change the base?
Changes from 7 commits
c280d86
82c3286
4560f60
6317d3a
8ae1d78
33f1d2e
def3e96
a143bc0
cfa578d
6835f81
30b287e
c973f2c
f218ffe
0ff2c7a
a2cecec
e999185
b4e4872
f33299f
6c1629c
4d989f0
eea6e79
a09642a
6de1489
2277f3a
b08eb88
3e29301
52275d9
e27e10c
d3da340
2d99bad
4cb291d
70937c2
7c04731
3cccb17
9a66338
c9bd281
0168858
e18f8b8
379fdc1
028629d
c241b13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| { | ||
| "sdk": { | ||
| "version": "5.0.300", | ||
| "rollForward": "minor" | ||
| } | ||
| } | ||
| "sdk": { | ||
| "version": "6.0.200", | ||
| "rollForward": "latestMinor" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,22 @@ | ||
| module Sample.App | ||
|
|
||
| open Browser | ||
| open Fable.Core | ||
| open Elmish | ||
| open Elmish.HMR | ||
| open Lit | ||
| open Lit.Elmish | ||
| open Components | ||
|
|
||
| let private hmr = HMR.createToken() | ||
| let private hmr = HMR.createToken () | ||
|
|
||
| Clock.register() | ||
| Clock.register () | ||
|
|
||
| let init() = | ||
| let init () = | ||
| { Value = "World" | ||
| ShowClock = true | ||
| ShowReact = true }, Cmd.none | ||
| ShowReact = true }, | ||
| Cmd.none | ||
|
|
||
| let update msg model = | ||
| match msg with | ||
|
|
@@ -23,24 +25,34 @@ let update msg model = | |
| | ToggleReact v -> { model with ShowReact = v }, Cmd.none | ||
|
|
||
| let view model dispatch = | ||
| html $""" | ||
| html | ||
| $""" | ||
| <div class="vertical-container" style="margin-left: 2rem;"> | ||
|
|
||
| <button class="button" | ||
| style="margin: 1rem 0" | ||
| @click={Ev(fun _ -> not model.ShowReact |> ToggleReact |> dispatch)}> | ||
| {if model.ShowReact then "Hide" else "Show"} React | ||
| {if model.ShowReact then | ||
| "Hide" | ||
| else | ||
| "Show"} React | ||
| </button> | ||
|
|
||
| {if not model.ShowReact then Lit.nothing | ||
| else ReactLitComponent model.ShowClock} | ||
| {if not model.ShowReact then | ||
| Lit.nothing | ||
| else | ||
| ReactLitComponent model.ShowClock} | ||
|
|
||
| <br /> | ||
|
|
||
| {elmishNameInput model.Value (ChangeValue >> dispatch)} | ||
| {LocalNameInput()} | ||
|
|
||
| {ClockDisplay model dispatch} | ||
|
|
||
| <user-profile .name={model.Value} age="25"></user-profile> | ||
|
|
||
| <element-with-controller><element-with-controller> | ||
| </div> | ||
| """ | ||
|
|
||
|
|
@@ -49,10 +61,30 @@ let view model dispatch = | |
| // |> Program.run | ||
|
|
||
| [<LitElement("sample-app")>] | ||
| let App() = | ||
| Hook.useHmr(hmr) | ||
| let _ = LitElement.init(fun config -> | ||
| config.useShadowDom <- false | ||
| ) | ||
| let model, dispatch = Hook.useElmish(init, update) | ||
| view model dispatch | ||
| let App () = | ||
| Hook.useHmr (hmr) | ||
| let _ = LitElement.init (fun config -> config.useShadowDom <- false) | ||
| let model, dispatch = Hook.useElmish (init, update) | ||
| view model dispatch | ||
|
|
||
| [<Emit("String")>] | ||
| let StringCtor: obj = jsNative | ||
|
|
||
| registerElement "user-profile" JsInterop.jsConstructor<ClassComponents.UserProfile> { | ||
|
AngelMunoz marked this conversation as resolved.
Outdated
|
||
| property "name" { | ||
| use_attribute | ||
| use_type StringCtor | ||
| } | ||
|
|
||
| property "age" { | ||
| use_attribute | ||
| use_type JS.Constructors.Number | ||
| } | ||
|
|
||
| css $"p {{ color: red; }}" | ||
| } | ||
|
|
||
| registerElement "element-with-controller" JsInterop.jsConstructor<ClassComponents.ElementWithController> { | ||
| css $"p {{ color: red; }}" | ||
| css $"li {{ color: rebeccapurple; }}" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bit concerned that the class and the registration (with the props, styles...) are so far away and the compiler cannot tell you when you misspell a prop name. But let's see how it goes (it's true the compiler doesn't check the prop names in the html templates either).
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is too far fetched from what current lit users do, but I share this concern arguably I think it would be nice if we stick slightly more close to lit's semantics so people don't have to learn Fable.Lit + Lit |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| module ClassComponents | ||
|
|
||
| open Lit | ||
| open Fable.Core | ||
| open Fetch | ||
|
|
||
| type MouseController = | ||
| inherit ReactiveController | ||
| abstract x: float | ||
| abstract y: float | ||
| type Post = {| usedId: int; id: int; title: string; body: string |} | ||
|
|
||
| [<Import("MouseController", from="./controllers.js"); Emit("new $0($1)")>] | ||
| let private createMouseController (host: LitElement): MouseController = jsNative | ||
|
AngelMunoz marked this conversation as resolved.
Outdated
|
||
|
|
||
| type ApiController(host: LitElement) as this = | ||
| do | ||
| host.addController(this) | ||
|
AngelMunoz marked this conversation as resolved.
Outdated
|
||
|
|
||
| member val Posts: Post array = Array.empty with get, set | ||
| member val Page: int = 1 with get, set | ||
| member val Limit: int = 10 with get, set | ||
|
|
||
| member _.FetchUsers(?page: int, ?limit: int) = | ||
| let page = defaultArg page this.Page | ||
| let limit = defaultArg limit this.Limit | ||
| promise { | ||
| let! posts = | ||
| promise { | ||
| let! result = tryFetch $"https://jsonplaceholder.typicode.com/posts?_page={page}&_limit={limit}" [] | ||
| match result with | ||
| | Ok res -> | ||
| let! result = res.json() :?> JS.Promise<Post array> | ||
| return result | ||
| | Error err -> return Array.empty<Post> | ||
| } | ||
|
|
||
| this.Posts <- posts | ||
| host.requestUpdate() | ||
| } | ||
|
|
||
| member _.NextPage() = | ||
| this.Page <- this.Page + 1 | ||
| this.FetchUsers() |> Promise.start | ||
|
|
||
| interface ReactiveHostConnected with | ||
| member this.hostConnected(): unit = | ||
| this.FetchUsers() |> Promise.start | ||
|
|
||
| type UserProfile() = | ||
| inherit LitElement() | ||
|
|
||
| let name = "" | ||
| let age = 0 | ||
|
|
||
| override _.render() = | ||
| html $"<p>{name} - {age}</p>" | ||
|
|
||
| type ElementWithController() as this = | ||
| inherit LitElement() | ||
|
|
||
| let mouse = createMouseController this | ||
| let api = ApiController this | ||
|
|
||
| let posts (post: Post) = | ||
| html $"<li>{post.title}</li>" | ||
|
|
||
| override _.render() = | ||
| html $""" | ||
| <p>{mouse.x} - {mouse.y}</p> | ||
| <p>Page: {api.Page}</p> | ||
| <button @click={fun _ -> api.NextPage()}>Next Page</button> | ||
| <ul>{Lit.mapUnique (fun (p: Post) -> $"{p.id}") posts api.Posts}</li> | ||
| """ | ||
Uh oh!
There was an error while loading. Please reload this page.