Giter VIP home page Giter VIP logo

ui's Introduction

WebSharper.UI

Join the chat at https://gitter.im/intellifactory/websharper.ui.next

WebSharper.UI is a UI library featuring a dataflow layer for expressing time-varying values. It integrates with DOM and makes it simple to construct animated UI in the browser.

Note: WebSharper.UI is the new name of WebSharper.UI.Next. If you have been using the latter, we recommend that you upgrade to UI.

Acknowledgements

This design is a result of vibrant discussion and experimentation. The list of people who have contributed time, ideas and code includes:

  • Simon Fowler
  • Anton Tayanovskyy
  • Andras Janko
  • Loic Denuziere
  • Adam Granicz
  • Vesa Karvonen

ui's People

Contributors

btamas2000 avatar cata avatar diegopego avatar ghuntley avatar gitter-badger avatar granicz avatar gygerg avatar jand42 avatar jooseppi12 avatar mrtank avatar qwe2 avatar simonjf avatar t0yv0 avatar tarmil avatar teo-tsirpanis 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ui's Issues

Add On.* event handlers (via the generate script)

Event handler attributes should be typed more precisely with their appropriate args arguments.

One proposal is to place these under a separate class (On) for better discoverability and possible code reuse across client/server code.

Q: Time based events on Var change and/or onblur equivalents

Sorry if this the wrong place to ask. I am just getting up to speed with Websharper and UI.Next.

I have a form that I want to auto-save when the user leaves a textbox (onblur) and/or after a certain amount of time not-typing (whilst focussed in a textbox). I don't see anything obvious in the API docs for Var or Doc.Input that can aid me. Any advice?

Many issues with Doc.FloatInput and Doc.IntInput

The most obvious problem is with Doc.FloatInput. You can't enter a float value, e.g. "1.5", because as soon as you type "1.", the text is immediately replaced by "1". This is because "1." is successfully parsed and the var is updated, but then because the var was updated, the value is immediately formatted (using string x) and this simplifies the text to "1".

In fact, there are several usability problems with Doc.FloatInput and Doc.IntInput. Here are the requirements for numeric input as I see them:

  • Whatever you type, the code should never change that text. If I enter " 007.00", that's what I should see. It should never be simplified. This is true for both float and int input.
  • Empty input should be supported. If a dialog requires the user to enter a value, and there is no natural default value, the input field should initially be empty. The field can become empty again if the user deletes the text. And if the user just enters some spaces, this is empty too (and should not be simplified).
  • A dialog with an OK or Apply button needs to know if the input field is currently invalid. The code as it stands today sets the var to be the last valid value, but the input field may have been changed since then and may now be invalid.
  • Values need to be parsed correctly. Currently Doc.FloatInput uses parseFloat, and Doc.IntInput uses parseInt, but these are not the right functions to used (see here and here).

Here is my code for addressing these requirements.

First a helper function:

[<JavaScript>]
module String =
    /// The string is zero or more spaces.
    let blank (text: string) = text |> Seq.forall ((=) ' ')

Now the parsing:

[<Inline "Number($x)">]
let number x = X<float>

let (|ValidFloat|InvalidFloat|BlankFloat|) (text: string) =
    if String.blank text then BlankFloat
    else
        let x = number text
        if JS.IsNaN x then InvalidFloat else ValidFloat x

let (|ValidInt|InvalidInt|BlankInt|) text =
    match text with
    | _ when text |> Seq.exists ((=) '.') -> InvalidInt
    | ValidFloat x -> ValidInt (int x)
    | InvalidFloat -> InvalidInt
    | BlankFloat -> BlankInt

The var that we want to bind to the input field is of type FormattedText<float> or FormattedText<int>.

type FormattedText<'t> =
    | ValidText of text: string * value: 't
    | InvalidText of text: string
    | BlankText of spaces: string

Here is the code to connect the input field to the var:

let text = function
    | ValidText (text, _) -> text
    | InvalidText text -> text
    | BlankText text -> text

let formattedInput attrs tryParse (var: IRef<_>) =
    inputAttr [
        Attr.Concat attrs
        attr.typ "text"
        Attr.CustomValue var text (tryParse >> Some)
        Attr.DynamicClass "invalid-format" var.View (function InvalidText _ -> true | _ -> false)
    ] []

I like to show invalid input by changing the background color of the input field, which is what the Attr.DynamicClass is doing. This is optional of course, and this attribute could be added by the caller instead, as part of the attrs argument.

Finally here are my alternatives for Doc.FloatInput and Doc.IntInput. The filter function allows you to constrain the value, e.g. to require a positive number.

let floatInput attrs filter (var: IRef<_>) =
    let tryParse text =
        match text with
        | ValidFloat x when filter x -> ValidText (text, x)
        | BlankFloat -> BlankText text
        | _ -> InvalidText text
    formattedInput attrs tryParse var

let intInput attrs filter (var: IRef<_>) =
    let tryParse text =
        match text with
        | ValidInt x when filter x -> ValidText (text, x)
        | BlankInt -> BlankText text
        | _ -> InvalidText text
    formattedInput attrs tryParse var

Note that since tryParse is supplied to formattedInput, we can support other situations, such as parsing math expressions, e.g. "1.4 + 2/3". And since FormattedText<'t> is generic, other types could be supported, e.g. dates.

Here is an example usage. My application is CAD for mechanical design, and here I want the user to enter a setback distance for a chamfer, which will be applied to some edges:

let event f = fun _ _ -> f ()

let chamfer =
    let setback = Var.Create (BlankText "")

    let submit = Submitter.Create (View.Map Some setback.View) None
    submit.View
    |> View.Sink (function
        | Some (ValidText (_, setback)) -> JS.Alert (string setback)
        | _ -> ())

    let input = floatInput [attr.placeholder "distance"] (fun x -> x >= 0.) setback
    Template.ChamferDialog.Doc (
        Setback = [input],
        OnApply = event submit.Trigger)

Task does not have property "ContentFiles"

/Users/h/dev/suave/suave/packages/WebSharper/build/WebSharper.targets: Error: Error executing task WebSharperTask: Task does not have property "ContentFiles" defined (WebSharper)

When upgrading (paket update) and building Suave with latest releases.

Templating: allow an element to be both a hole and a template

Especially useful to specify the template for the content of that particular hole. For example:

<table>
  <tbody data-hole="TableBody" data-children-template="Row">
    <tr><td>${Text}<td></tr>
  </tbody>
</table>
type Template = Template<"index.html">

let elements = ListModel.Create ["first row"; "second row"]

Template.Doc(
  TableBody = [
    ListModel.View elements
    |> Doc.Convert (fun text -> Template.Row.Doc(Text = text))
  ]
)

`client` in Warp app fails runtime

This fails on the latest UI.Next and Warp (not yet released, compiled from source):

open WebSharper
open WebSharper.UI.Next
open WebSharper.UI.Next.Html

[<JavaScript>]
module Client =
    open WebSharper.JavaScript

    let SayHi (e: Dom.Event) =
        JS.Alert "Hi!"

    let SayHello () =
        buttonAttr [Client.Attr.Handler "click" SayHi] [text "click me"]

...
Warp.CreateApplication (fun ctx endpoints ->
    match endpoints with
    | Endpoints.Home ->
        Warp.Doc(
            Body = [
                div [p [text "testing"]]
                client <@ Client.SayHello() @>
            ]
        )

Missing default attribute for Doc.CheckBox

From @kowalgta on June 16, 2015 15:29

It seems that there should be a default attribute for single CheckBox:
https://github.com/intellifactory/websharper.ui.next/blob/master/WebSharper.UI.Next/Doc.fs

What is now:

Attr.Concat [
                yield! attrs
                yield Attr.DynamicProp "checked" chk.View
            ]

What should be:

Attr.Concat [
                yield! attrs
                yield Attr.Create "type" "checkbox"
                yield Attr.DynamicProp "checked" chk.View
            ]

Copied from original issue: dotnet-websharper/core#434

Questions about Var and View updates

Hi,

I was wondering what are your thoughts on:

  1. preventing spurious Var updates:
  • Currently, Var.Set will trigger an update of the corresponding View even if the current Var value is the same with the one being set.
  • As calls to Var.Set can be triggered as a result of external events, one currently has to check the Var value prior to setting a new one, in order to prevent spurious updates.

I was wondering if you would be partial to introducing an internal check for equality in the Var.Set function
If so, I can create a pull request.

  1. preventing spurious View updates:
  • Currently, there are no equality checks in the various Snap functions (Map, etc.) This means that, when composing Views, one can end up with unnecessary updates on the resulting view.
    For example, given a function f: a-> b that can yield the same b for distinct values of a and a view A: View, should one create a view B as View.Map f A, one would observe spurious View B updates (i.e successive updates of View B, say with the values bx and by even though bx = by )

Given my current understanding of UI.Next, I think addressing this would require changing the Snap implementation. This would be helpful in simplifying client code (right now I need to perform these checks in functions generating the UI, to avoid unnecessarily recreating DOM elements)

Are you open to such a change?

Snap.SnapshotOn when both snaps change at the same time

I'm trying to control update propagation of views so that update only occurs when the value has changed.

Here is an example. I am dragging the mouse and I want to convert a view of XY positions into a view of distances from the mouse-down position, where these distances are snapped to the nearest value of 10.

let snap (dist: float) x = dist * Math.Round (x / dist)
let inline private sq x = x * x
let dist (stX, stY) (enX, enY) =
    sq (stX - enX) + sq (stY - enY) |> float |> sqrt |> snap 10

// set the value only if it has changed
let inline assign (var: Var<_>) value = if value <> var.Value then var.Value <- value

let distView down pos = // result is View<float>
    let state = Var.Create 0.
    pos // input is View<int * int>
    |> View.Bind (fun pos ->
        assign state (dist down pos)
        state.View)
    |> View.SnapshotOn state.Value state.View

As you drag away from the mouse-down position, many XY positions will produce the first snapped distance, 0, and then many more XY positions will produce the next distance, 10, and then 20, and so on.

The usual behavior of computed views (View.Map, View.Bind, etc.) is that whenever input views are updated (their Snap becomes obsolete), this marks the computed view snap as obsolete, and so on, all the way up the dependency tree. A new snap value will be recalculated if and when it is next demanded.

This is even true for View.UpdateWhile. When the first view is false, the computed view will nevertheless update each time the second view is updated, and return the last captured value of the second view over and over.

This is fine in that the values are correct, but if the calculation done inside a View.Map function is expensive, we don't want to repeat the calculation when the value hasn't changed. In my case, the calculation done with the distance (not shown here) is expensive, but this needs to be efficient since it is going on while the user is dragging the mouse.

In the code above, at first glance it looks like View.Bind returns state.View, which doesn't change all the time, but in fact View.Bind creates a new computed view, which just happens to have the value of state.View in this example. So even if state.View hasn't changed, View.Bind will create a new snap for each new XY position, copying the value for this snap out of state.View.

So my idea was to use View.SnapshotOn at the end, so that the resulting view is only updated when state.View has actually changed value. But sadly it doesn't work, and I only get the default value.

I believe the problem might be in Snap.SnapshotOn. Here's the code:

let SnapshotOn sn1 sn2 =
    match sn1.State, sn2.State with
    | _, Forever y -> CreateForever y
    | _ ->
        let res = Create ()
        let v = ref None
        let triggered = ref false

        let obs () =
            v := None
            MarkObsolete res

        let cont () =
            if !triggered then
                match !v with
                | Some y when IsForever sn2 -> MarkForever res y
                | Some y -> MarkReady res y
                | _ -> ()
        When sn1 (fun x -> triggered := true; cont ()) obs
        When sn2 (fun y -> v := Some y; cont ()) ignore
        res

I'm not sure what this triggered test is all about, but it might be the problem. As I see it, we want the value from sn2 to be applied to res, and we want res to be made obsolete when sn1 becomes obsolete.

Perhaps I don't understand the code. Am I misusing View.SnapshotOn or is there a bug?

CDATA sections are not treated correctly in templating

For instance, this block:

    <script>
         <![CDATA[
              $(function(){$("#slides").superslides({animation:"fade"});})
         ]]>
    </script>

gets rendered as:

    <script>

              $(function(){$(&quot;#slides&quot;).superslides({animation:&quot;fade&quot;});})

    </script>

It is not possible to register events on input that occur after the var has changed

The Doc.Input family currently uses the keyup event (due to IE8 compatibility) to change the value of the underlying IRef, which results in the user not being able to register any events that occur after the value of the IRef has changed. The order of the same events is not guaranteed by the spec. Probably should use keypressed instead if no compatibility issue arises.

let newName = Var.Create ""

Doc.Concat [
    Doc.Input [
        on.keyUp (fun el ev -> 
            if ev?keyCode = 13 then
                Var.Set newName ""
        )
    ] newName
    textView newName.View
]
|> Doc.RunById "main"

Here newName doesn't get to set to empty even if we use the keyup event to do it.

Add Doc.SelectOptional

Example:

type Country = HU | FR
let v = Var.Create None
SelectOptional [] "Choose a country"
    (function HU -> "Hungary" | FR -> "France")
    [HU; FR] v

yields

<select>
    <option value=0>Choose a country</option>  <!-- sets v to None -->
    <option value=1>Hungary</option>           <!-- sets v to Some HU -->
    <option value=2>France</option>            <!-- sets v to Some FR -->
</select>

The ability to create Doc element of hypertext

Hi!
I have the string with html markup, an I want to cretae Doc elment from it like this:

Doc.FromHtm "<div><p>.....</p>.....</div>"

As I understand that this is not possible right now. Ok, what is not possible to accurately sew, I tried to roughly nail using jquery:

JQuery.JQuery.Of( "." + class'name ).First().Html(html'content)

But to call this code, I need to specify an event handler for the Doc element. But it is not implemented in UI.Next.
I tried to track changes of a model with a given CSS class asynchronously:

let inbox'post'after'render = MailboxProcessor.Start(fun agent -> 
            let rec loop (ids'contents : Map<int,string>) : Async<unit> = async {
                // try to recive event with new portion of data
                let! new'ids'contents = agent.TryReceive 100
                // calculate the state of the agent
                let ids'contents = 
                    // merge Map's
                    (   match new'ids'contents with
                        | None -> ids'contents
                        | Some (new'ids'contents) -> 
                            new'ids'contents @ (Map.toList ids'contents)
                            |> Map.ofList )                    
                    |> Map.filter( fun id content ->
                        // calculate CSS class name
                        let class'name = post'text'view'class'name id
                        // change it's contents of html
                        JQuery.JQuery.Of( "." + class'name ).First().Html(content).Size() = 0)
                // accept the state of the agent
                return! loop ids'contents }
            loop Map.empty )         

and then, for example for one element:

inbox'post'after'render.Post [id, content]

But it is too difficult, unreliable and not really working.
Please give me an idea how to solve the problem if possible. Thanks in advance!

Add support for event handlers that can use a view's value

Something like this:

module Attr =
    val HandlerView : event: string -> View<'T> -> (Element -> #Event -> 'T -> unit) -> Attr
    // Existing API for comparison:
    val Handler     : event: string             -> (Element -> #Event       -> unit) -> Attr

Of course the callback should only be called on event, not on new value in the view.

Also add corresponding versions of callback-based elements:

module Doc =
    val Doc.ButtonView : caption: string -> seq<Attr> -> View<'T> -> ('T   -> unit) -> Elt
    // Existing API for comparison:
    val Doc.Button     : caption: string -> seq<Attr>             -> (unit -> unit) -> Elt
    // and the same for Doc.Link

warning MSB3061: Unable to delete file ...

I reported this in the WebSharper Forum. I can now reproduce it quite simply with a new Visual Studio solution and only two projects.

I'm using Visual Studio 2015, if that makes any difference, and .NET 4.6 with F# 4.0.

It seems I can't upload a ZIP file of the solution (file type not supported), so here's how to recreate it:

  1. Create a new solution.
  2. Create WebSharper Library project called Library.
  3. Create a Visual F# Library project called Core.
  4. Add a project reference from Library to Core.
  5. I am using .NET 4.6 with F# 4.0, and that's how Core was set up. So I had to change the project properties for the Library project to be the same (the WebSharper template created the project using F# 3.0).
  6. Add packages WebSharper and WebSharper.UI.Next to the Library project.
  7. Verify that you can build the solution.

To set up the conditions for the problem, do this:

  1. Clean the solution.
  2. Build the Core project only.
  3. Open the Main.fs source file from the Library project.

At this point, a security dialog will appear saying: You have opened a source code file in a project that references a type provider. It shows that the type provider assembly is WebSharper.UI.Next.Templating.dll. Press the Enable button.

To produce the problem:

  1. Close Visual Studio.
  2. Reopen the solution and wait 20 seconds.
  3. Clean the solution.

You will find that Core.dll is locked:

warning MSB3061: Unable to delete file "<path>\Core\bin\Debug\Core.dll". Access to the path '<path>\bin\Debug\Core.dll' is denied.

The problem only occurs if Main.fs is open, so if you close Main.fs and restart Visual Studio, you will now be able to clean the solution successfully.

Also, if you expand the project references for the Library project and remove the reference to WebSharper.UI.Next.Templating, the problem will not occur.

VS 2015 WebSharper Missing Item Templates

On Visual Studio 2015 Enterprise / Windows Server 2012r2 Hyper-V SharePoint developer machine with 3.4.8.186 vsix and WebSharper.UI.Next project not seeing item templates from within any of the WebSharper project types. Ran devenv / with no difference - still Add New Item -> "No items found". Please advise.

Thanks,

Henri
NOTE: Login to websharper forum page broken (IE, Chrome) using github provider, which is reason for using this location. Sorry.

Routing is inconsistent in Firefox

Firefox has a different implementation of the window.location.hash getter than other browsers, resulting in possibly inconsistent parts if the hashed URI contains special characters. It surely is broken if the escaped fragment contains /.

An alternative way would be to use (window.location.href.split("#")[1] || "") to get the hash.

Related issue on bugzilla:
https://bugzilla.mozilla.org/show_bug.cgi?id=483304

Issue adding Websharper.UI.Next to client-server template project

Hi,
I installed the latest WebSharper VS Extension (3.0.46.130-rc1) on VS2013, created a new Client-Server template project and added the latest WebSharper.UI.Next (3.0.100.30-alpha) from Nuget
The first build (with no modifications to the project) was successful, but now if I just rebuild the solution I get the error:

Error 2 The type provider constructor has thrown an exception: Could not load file or assembly 'WebSharper.Main, Version=3.0.0.0, Culture=neutral, PublicKeyToken=dcd983dec8f76a71' or one of its dependencies. Impossibile trovare il file specificato. parse error FS3053

So I tried cloning the WebSharper.UI.Next repository from GitHub and building it with

.\build.cmd

fsi.exe --exec .\build.fsx

but I got this:

[Error] C:\Users\Marco\AppData\Local\Temp\tmp3E63.tmp(1,1): System.IO.FileNotFoundException: Impossibile caricare il file o l'assembly 'WebSharper.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=451ee5fa653b377d' o una delle relative dipendenze. Impossibile trovare il file specificato. System.Exception: Non-zero exit code: 1 in <StartupCode$IntelliFactory-Build>[email protected](String message) in IntelliFactory.Build.WebSharperUtility.Execute(AppDomain dom, String exe, IEnumerable``1 args) in IntelliFactory.Build.WebSharperProject.build() in <StartupCode$IntelliFactory-Build>[email protected](AppDomain dom) in IntelliFactory.Build.Solution.WithDomain[T](FSharpFunc``2 f) in IntelliFactory.Build.BuildTool.Dispatch(Solution sln) in <StartupCode$FSI_0002>.$FSI_0002.main@() in C:\Users\Marco\repo_github\websharper.ui.next\build.fsx:riga 23 Stopped due to error

The previous versions of WebSharper and UI.Next work fine.

Restore Attr.ClassDynPred

It was inadvertently removed when creating the Client sub-namespace. Also check if anything else is missing.

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.