Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c280d86
Fix a typo
BillHally Apr 11, 2022
82c3286
add styles in the Lit functions
AngelMunoz May 17, 2022
4560f60
Remove mention to --runFast in docs
alfonsogarciacaro May 27, 2022
6317d3a
Solutions for #31 and #39
alfonsogarciacaro May 27, 2022
8ae1d78
add extra types to LitElement
AngelMunoz May 29, 2022
33f1d2e
add computation expressions for Class LitElements
AngelMunoz May 29, 2022
def3e96
add class based examples
AngelMunoz May 29, 2022
a143bc0
update to .NET6
AngelMunoz May 30, 2022
cfa578d
update local tools
AngelMunoz May 30, 2022
6835f81
re-order things a bit
AngelMunoz May 30, 2022
30b287e
fix format and simplify CEs
AngelMunoz May 30, 2022
c973f2c
fix sample usage
AngelMunoz May 30, 2022
f218ffe
inline CE, address pr comments
AngelMunoz May 30, 2022
0ff2c7a
fix reactive controller call
AngelMunoz May 30, 2022
a2cecec
update definitions
AngelMunoz May 30, 2022
e999185
update lit version
AngelMunoz May 31, 2022
b4e4872
fix pr comments
AngelMunoz May 31, 2022
f33299f
add full bindings to directives
AngelMunoz May 31, 2022
6c1629c
add experimental module
AngelMunoz May 31, 2022
4d989f0
remove decorator experiment
AngelMunoz Jun 2, 2022
eea6e79
add elmish controller test
AngelMunoz Jun 2, 2022
a09642a
enable controllers + hook.GetController
AngelMunoz Jun 2, 2022
6de1489
Fixing self closing web component tags
leolorenzoluis Jul 7, 2022
2277f3a
Added Lit.mapiUnique
leolorenzoluis Jul 7, 2022
b08eb88
Publish 1.4.2
alfonsogarciacaro Jul 14, 2022
3e29301
Update templates.md
alfonsogarciacaro Jul 25, 2022
52275d9
Merge branch 'master' into experimental-class-bindings
AngelMunoz Jul 30, 2022
e27e10c
Merge branch 'master' into experimental-class-bindings
AngelMunoz Jul 30, 2022
d3da340
Update hook-components.md
JordanMarr Aug 1, 2022
2d99bad
Update hook-components.md
alfonsogarciacaro Aug 22, 2022
4cb291d
Fix #67
alfonsogarciacaro Aug 23, 2022
70937c2
Fix Github actions
alfonsogarciacaro Sep 5, 2022
7c04731
Merge branch 'master' into experimental-class-bindings
AngelMunoz Sep 14, 2022
3cccb17
change the name for consistency
AngelMunoz Sep 14, 2022
9a66338
add class docs
AngelMunoz Sep 14, 2022
c9bd281
add controllers docs
AngelMunoz Sep 14, 2022
0168858
add new pages to the menu
AngelMunoz Sep 14, 2022
e18f8b8
remove the effect controller
AngelMunoz Sep 14, 2022
379fdc1
fix the compilation issue
AngelMunoz Sep 14, 2022
028629d
add doc strings
AngelMunoz Sep 14, 2022
c241b13
add a couple more comments
AngelMunoz Sep 14, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@
"isRoot": true,
"tools": {
"fable": {
"version": "3.6.1",
"version": "3.7.12",
"commands": [
"fable"
]
},
"femto": {
"version": "0.9.0",
"version": "0.13.0",
"commands": [
"femto"
]
},
"fantomas": {
"version": "5.0.0-alpha-008",
"commands": [
"fantomas"
]
}
}
}
10 changes: 5 additions & 5 deletions global.json
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"
}
}
62 changes: 47 additions & 15 deletions sample/App.fs
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
Expand All @@ -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>
"""

Expand All @@ -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
Comment thread
AngelMunoz marked this conversation as resolved.
Outdated

registerElement "user-profile" JsInterop.jsConstructor<ClassComponents.UserProfile> {
Comment thread
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; }}"
Copy link
Copy Markdown
Member

@alfonsogarciacaro alfonsogarciacaro May 30, 2022

Choose a reason for hiding this comment

The 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).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The 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

}
74 changes: 74 additions & 0 deletions sample/ClassComponents.fs
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
Comment thread
AngelMunoz marked this conversation as resolved.
Outdated

type ApiController(host: LitElement) as this =
do
host.addController(this)
Comment thread
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>
"""
4 changes: 3 additions & 1 deletion sample/Sample.fsproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="ClassComponents.fs" />
<Compile Include="Clock.fs" />
<Compile Include="Types.fs" />
<Compile Include="Components.fs" />
Expand All @@ -16,6 +17,7 @@
<!-- <PackageReference Include="Fable.Lit.Elmish" Version="1.0.0-rc-001" /> -->
<!-- <PackageReference Include="Fable.Lit.React" Version="1.0.0-rc-001" /> -->
<PackageReference Include="Fable.Elmish.HMR" Version="4.3.1" />
<PackageReference Include="Fable.Fetch" Version="2.4.0" />
<PackageReference Include="Feliz" Version="1.52.0" />
</ItemGroup>
</Project>
3 changes: 2 additions & 1 deletion src/Lit/Lit.fsproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
<PackageId>Fable.Lit</PackageId>
Expand All @@ -17,6 +17,7 @@
<Compile Include="HMR.fs" />
<Compile Include="Hook.fs" />
<Compile Include="LitElement.fs" />
<Compile Include="LitElementCE.fs" />
</ItemGroup>
<ItemGroup>
<Content Include="*.fsproj; *.fs; package.json" PackagePath="fable\" />
Expand Down
62 changes: 45 additions & 17 deletions src/Lit/LitElement.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,63 @@ open Browser
open Browser.Types
open HMRTypes
open Lit
type Converter =
abstract fromAttribute: JS.Function with get, set
abstract toAttribute: JS.Function with get, set

type PropConfig =
abstract ``type``: obj with get, set
abstract attribute: U2<string, bool> with get, set
abstract state: bool with get, set
abstract reflect: bool with get, set
abstract noAccessor: bool with get, set
abstract converter: Converter with get, set
abstract hasChanged: JS.Function with get, set

// LitElement should inherit HTMLElement but HTMLElement
// is still implemented as interface in Fable.Browser
[<Import("LitElement", "lit")>]
type LitElement() =
member _.hasUpdated with get() : bool = jsNative
member _.isUpdatePending with get() : bool = jsNative
/// Returns a promise that will resolve when the element has finished updating.
member _.updateComplete with get(): JS.Promise<bool> = jsNative
/// Node or ShadowRoot into which element DOM should be rendered. Defaults to an open shadowRoot.
member _.renderRoot: HTMLElement = jsNative
member _.shadowRoot: ShadowRoot = jsNative
member _.isConnected: bool = jsNative
member _.connectedCallback(): unit = jsNative
member _.disconnectedCallback(): unit = jsNative
member _.requestUpdate(): unit = jsNative
/// Returns a promise that will resolve when the element has finished updating.
member _.updateComplete: JS.Promise<unit> = jsNative
member _.attributeChangedCallback(name: string, oldValue: string, newValue: string): unit = jsNative
member _.requestUpdate(?name: string, ?oldValue: obj, ?options: PropConfig): unit = jsNative
member _.addController(controller: ReactiveControllerBase): unit = jsNative
member _.removeController(controller: ReactiveControllerBase): unit = jsNative
member _.createRenderRoot(): unit = jsNative
member _.enableUpdating(requestedUpdate: bool): unit = jsNative
member _.getUpdateComplete(): JS.Promise<bool> = jsNative
member _.firstUpdated(updated: JS.Map<string, string>): unit = jsNative
member _.shouldUpdate(updated: JS.Map<string, string>): unit = jsNative
member _.update(updated: JS.Map<string, string>): unit = jsNative
member _.updated(updated: JS.Map<string, string>): unit = jsNative
member _.willUpdate(updated: JS.Map<string, string>): unit = jsNative
member _.performUpdate(): JS.Promise<unit> = jsNative
member _.scheduleUpdate(): JS.Promise<unit> = jsNative
abstract render: unit -> TemplateResult
override _.render() = jsNative

static member observedAttributes with get(): bool = jsNative
static member finalized with get(): bool = jsNative
static member elementProperties with get(): JS.Map<string, PropConfig> = jsNative
static member shadowRootOptions with get(): ShadowRootInit = jsNative
static member elementStyles: CSSResult array = jsNative

static member properties with get(): JS.Map<string, PropConfig> = jsNative
static member styles with get(): CSSResult array = jsNative
static member getPropertyOptions(name): PropConfig = jsNative
static member addInitializer(instance: LitElement): unit = jsNative
static member finalize(): unit = jsNative
static member finalizeStyles(styles: CSSResult array): unit = jsNative
static member createProperty(name: string, config: PropConfig): unit = jsNative

// Compiler trick: we use a different generic type, but they both
// refer to the same imported type
Expand All @@ -42,19 +83,6 @@ module private LitElementUtil =
let [<Global>] Array = obj()
let [<Global>] Object = obj()

type Converter =
abstract fromAttribute: JS.Function with get, set
abstract toAttribute: JS.Function with get, set

type PropConfig =
abstract ``type``: obj with get, set
abstract attribute: U2<string, bool> with get, set
abstract state: bool with get, set
abstract reflect: bool with get, set
abstract noAccessor: bool with get, set
abstract converter: Converter with get, set
abstract hasChanged: JS.Function with get, set

let isNotNull (x: obj) = not(isNull x)
let isNotReferenceEquals (x: obj) (y: obj) = not(obj.ReferenceEquals(x, y))
let failInit() = failwith "LitElement.init must be called on top of the render function"
Expand Down Expand Up @@ -216,7 +244,7 @@ type LitHookElement<'Props, 'Ctrls>(init: obj -> unit) =
abstract renderFn: JS.Function with get, set
abstract name: string

member _.render() =
override _.render() =
_hooks.render()

member _.disconnectedCallback() =
Expand Down
Loading