====
Demo
====
Time Flies Like an Arrow
========================
.. raw:: html
The Timeflies example (`source code
`_)
implements the classic `Time
Flies `_
example from `RxJS `_.
.. raw:: html
In the Time files demo the stream of mouse moves are transformed into a
stream of letters where each letter is delayed according to its
position.
The ``Model`` type holds data that you want to keep track of while the
application is running.
.. code:: fsharp
type Model = {
Letters: Map
}
The ``Msg`` type defines what events/actions can occur while the
application is running. The state of the application changes *only* in
reaction to these events
.. code:: fsharp
type Msg =
| Letter of int * string * int * int
The update function computes the next state of the application based on
the current state and the incoming messages
.. code:: fsharp
let update (msg : Msg) (currentModel : Model) : Model =
match currentModel.Letters, msg with
| _, Letter (i, c, x, y) ->
{ currentModel with Letters = currentModel.Letters.Add (i, (c, x, y)) }
The view function renders the model and the result will be handled over
to React. It produces a div element and then takes each letter in the
Map and adds a span element containing the letter. The span element will
have the top/left properties set so that the letter is rendered at the
correct position on the page.
.. code:: fsharp
let view (model : Model) (dispatch : Dispatch) =
let letters = model.Letters
div [ Style [ FontFamily "Consolas, monospace"; Height "100%"] ]
[
[ for KeyValue(i, (c, x, y)) in letters do
yield span [ Key (c + string i); Style [Top y; Left x; Position "fixed"] ]
[ str c ] ] |> ofList
]
The init funciton produces an empty model.
.. code:: fsharp
let init () : Model =
{ Letters = Map.empty }
Helper code to render the letters at the correct position on the page.
.. code:: fsharp
let getOffset (element: Browser.Element) =
let doc = element.ownerDocument
let docElem = doc.documentElement
let clientTop = docElem.clientTop
let clientLeft = docElem.clientLeft
let scrollTop = Browser.window.pageYOffset
let scrollLeft = Browser.window.pageXOffset
int (scrollTop - clientTop), int (scrollLeft - clientLeft)
let container = Browser.document.querySelector "#elmish-app"
let top, left = getOffset container
Message stream (expression style) that transforms the stream of mouse moves
into a stream of letters where each letter is delayed according to its position
in the stream.
.. code:: fsharp
let stream (model : Model) (msgs: IAsyncObservable) =
asyncRx {
let chars =
Seq.toList "TIME FLIES LIKE AN ARROW"
|> Seq.mapi (fun i c -> i, c)
let! i, c = AsyncRx.ofSeq chars
yield! AsyncRx.ofMouseMove ()
|> AsyncRx.delay (100 * i)
|> AsyncRx.map (fun m -> Letter (i, string c, int m.clientX + i * 10 + 15 - left, int m.clientY - top))
} |> AsyncRx.tag "msgs"