diff --git a/WebSharper.UI.Tests/Main.fs b/WebSharper.UI.Tests/Main.fs index 3931e671..944a61f3 100644 --- a/WebSharper.UI.Tests/Main.fs +++ b/WebSharper.UI.Tests/Main.fs @@ -567,21 +567,26 @@ module Main = ] |> Doc.RunAppend JS.Document.Body let rv = Var.Create { x = "red"; y = { z = "green"; t = "test" } } - div [Attr.Style "color" rv.V.y.z] [ - text "Test text x.V: enter a color: " - Doc.Input [ - attr.style ("background: " + rv.V.y.z) - on.click (fun el ev -> - let f x = x // Inner generic function that fails to compile if this lambda is passed as an Expr. - // This checks that we are calling: - // Client.on.click : (Element -> Event -> unit) -> Attr - // and not: - // Html.on.click : Expr Event -> unit> -> Attr - () - ) - ] (rv.LensAuto(fun v -> v.y.z)) - Doc.PasswordBoxV [attr.style ("background: " + rv.V.y.z)] rv.V.y.z - text (" You typed: " + rv.V.y.z) + div [ + Attr.Style "color" rv.V.y.z + ] [ + p [] [ + text "Test text x.V: enter a color: " + Doc.Input [ + attr.style ("background: " + rv.V.y.z) + on.click (fun el ev -> + let f x = x // Inner generic function that fails to compile if this lambda is passed as an Expr. + // This checks that we are calling: + // Client.on.click : (Element -> Event -> unit) -> Attr + // and not: + // Html.on.click : Expr Event -> unit> -> Attr + () + ) + ] (rv.LensAuto(fun v -> v.y.z)) + Doc.PasswordBoxV [attr.style ("background: " + rv.V.y.z)] rv.V.y.z + ] + p [] [text (" You typed: " + rv.V.y.z)] V(ul [] (rv.V.y.z |> Seq.map (fun c -> li [] [text (string c)] :> Doc))).V + p [Attr.ClassPred "is-green" (rv.V.y.z = "green")] [text "this should be bordered with green iff you typed \"green\"."] ] |> Doc.RunAppend JS.Document.Body diff --git a/WebSharper.UI.Tests/index.html b/WebSharper.UI.Tests/index.html index 195cf6c2..4097457f 100644 --- a/WebSharper.UI.Tests/index.html +++ b/WebSharper.UI.Tests/index.html @@ -1,4 +1,4 @@ - + WebSharper.UI.Tests @@ -6,6 +6,9 @@ +
diff --git a/WebSharper.UI/Attr.Client.fs b/WebSharper.UI/Attr.Client.fs index 8a7837e5..acc3edf7 100644 --- a/WebSharper.UI/Attr.Client.fs +++ b/WebSharper.UI/Attr.Client.fs @@ -293,9 +293,6 @@ module Attr = let Style name value = As (Attrs.Static (fun el -> DU.SetStyle el name value)) - let Class name = - As (Attrs.Static (fun el -> DU.AddClass el name)) - let Animated name tr view attr = As (Attrs.Animated tr view (fun el v -> DU.SetAttr el name (attr v))) @@ -329,9 +326,19 @@ module Attr = (OnAfterRender (fun el -> callback el el?(id))) (DynamicCustom (fun el x -> el?(id) <- x) v) - let DynamicClass name view ok = + let DynamicClassPred name view = As (Attrs.Dynamic view (fun el v -> - if ok v then DU.AddClass el name else DU.RemoveClass el name)) + if v then DU.AddClass el name else DU.RemoveClass el name)) + + [)>] + let ClassPred name isSet = + As (Attrs.Static (fun el -> + if isSet then DU.AddClass el name else DU.RemoveClass el name)) + + let Class name = ClassPred name true + + let DynamicClass name view ok = + DynamicClassPred name (View.Map ok view) let DynamicPred name predView valView = let viewFn el (p, v) = diff --git a/WebSharper.UI/Attr.Client.fsi b/WebSharper.UI/Attr.Client.fsi index 9911c6e9..e7702883 100644 --- a/WebSharper.UI/Attr.Client.fsi +++ b/WebSharper.UI/Attr.Client.fsi @@ -72,7 +72,14 @@ module Attr = /// Sets a CSS class. val Class : name: string -> Attr + // new; V-enabled on `isSet` (turns it into DynamicClassPred) + val ClassPred : name: string -> isSet: bool -> Attr + + /// Dynamic variant of Style. + val DynamicClassPred : name: string -> isSet: View -> Attr + /// Sets a CSS class when the given view satisfies a predicate. + [] val DynamicClass : name: string -> view: View<'T> -> apply: ('T -> bool) -> Attr /// Sets an attribute when a view satisfies a predicate. diff --git a/WebSharper.UI/Macros.fs b/WebSharper.UI/Macros.fs index 4ca16a3f..4eb193bc 100644 --- a/WebSharper.UI/Macros.fs +++ b/WebSharper.UI/Macros.fs @@ -59,6 +59,7 @@ module internal Macros = | _ -> false let isV (m: Method) = m.Value.MethodName = "get_V" let stringT = NonGenericType (ty' "netstandard" "System.String") + let boolT = NonGenericType (ty' "netstandard" "System.Boolean") let objT = NonGenericType (ty' "netstandard" "System.Object") let viewModule = NonGeneric (ty "View") let varModule = NonGeneric (ty "Var") @@ -83,6 +84,7 @@ module internal Macros = let textViewFn = gen[] (meth "TextView" [viewOf stringT] docT) let attrDynFn = gen[] (meth "Dynamic" [stringT; viewOf stringT] attrT) let attrDynStyleFn = gen[] (meth "DynamicStyle" [stringT; viewOf stringT] attrT) + let attrDynClassFn = gen[] (meth "DynamicClassPred" [stringT; viewOf boolT] attrT) let docEmbedFn t = gen[t] (meth "EmbedView" [viewOf T0] docT) let lensFn t u = gen[t; u] (meth "Lens" [varOf T0; T0 ^-> T1; T0 ^-> T1 ^-> T0] (varOf T1)) let inputFn n t = gen[] (meth n [seqOf attrT; varOf t] eltT) @@ -280,6 +282,14 @@ module internal Macros = | V.Kind.Const _ -> MacroFallback | V.Kind.View e -> MacroOk (Call (None, clientAttrModule, attrDynStyleFn, [call.Arguments.[0]; e])) + type AttrClass() = + inherit Macro() + + override this.TranslateCall(call) = + match V.Visit stringT call.Arguments.[1] with + | V.Kind.Const _ -> MacroFallback + | V.Kind.View e -> MacroOk (Call (None, clientAttrModule, attrDynClassFn, [call.Arguments.[0]; e])) + type LensMeth() = inherit Macro() diff --git a/WebSharper.UI/Macros.fsi b/WebSharper.UI/Macros.fsi index f4c5abe7..4e2887f6 100644 --- a/WebSharper.UI/Macros.fsi +++ b/WebSharper.UI/Macros.fsi @@ -27,6 +27,7 @@ module internal Macros = [] type TextView = inherit WebSharper.Core.Macro [] type AttrCreate = inherit WebSharper.Core.Macro [] type AttrStyle = inherit WebSharper.Core.Macro + [] type AttrClass = inherit WebSharper.Core.Macro [] type ElementMixed = inherit WebSharper.Core.Macro [] type DocConcatMixed = inherit WebSharper.Core.Macro [] type LensMeth = inherit WebSharper.Core.Macro