Skip to content

Instantly share code, notes, and snippets.

@MangelMaxime
Created June 26, 2019 20:11
Show Gist options
  • Save MangelMaxime/76c5d55999e867a6292945ac2cdb1843 to your computer and use it in GitHub Desktop.
Save MangelMaxime/76c5d55999e867a6292945ac2cdb1843 to your computer and use it in GitHub Desktop.
"Pure Elmish" implementation of TIME FLIES LIKE AN ARROW example
html,
body {
font-size: 16px;
}
.main-container {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
}
.input {
padding: .25rem;
font-size: 16px;
width: 250px;
margin-bottom: 1rem;
}
module Thoth.RandomUser
open Fable.Core
open Fable.React
open Fable.React.Props
open Browser
open Elmish
open Elmish.React
let getOffset (element: Browser.Types.Element) =
let doc = element.ownerDocument
let docElem = doc.documentElement
let clientTop = docElem.clientTop
let clientLeft = docElem.clientLeft
let scrollTop = window.pageYOffset
let scrollLeft = window.pageXOffset
int (scrollTop - clientTop), int (scrollLeft - clientLeft)
let container = document.querySelector "#elmish-app"
let top, left = getOffset container
// The model holds data that you want to keep track of while the
// application is running
type Model = {
Letters: Map<int, string * int * int>
}
// 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
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
let update (msg : Msg) (currentModel : Model) =
match currentModel.Letters, msg with
| _, Letter (i, c, x, y) ->
{ currentModel with Letters = currentModel.Letters.Add (i, (c, x, y)) }, Cmd.none
let onMouseMove (f: Types.Event->unit) =
window.addEventListener("mousemove", f)
{ new System.IDisposable with
member __.Dispose() =
window.removeEventListener("mousemove", f) }
type TextViewProps =
{
Model : Model
Dispatch : Dispatch<Msg>
}
let textView =
FunctionComponent.Of(
fun (props : TextViewProps) ->
Hooks.useEffectDisposable(fun () ->
onMouseMove (fun ev ->
props.Model.Letters
|> Map.toList
|> List.iter (fun (index, (c, x, y)) ->
window.setTimeout(fun () ->
let ev = ev :?> Types.MouseEvent
let clientX = int ev.clientX + index * 10 + 15 - left
let clientY = int ev.clientY - top
(index, c, clientX, clientY)
|> Letter
|> props.Dispatch
, 100 * index)
|> ignore
)
)
, [| props.Model.Letters |])
let letters =
props.Model.Letters
|> Map.toList
|> List.map (fun (index, (c, x, y)) ->
span [
Style
[
Top y
Left x
Position (PositionOptions.Fixed)
]
]
[ str c ]
)
|> ofList
div [ Style [ FontFamily "Consolas, monospace"; Height "100%"] ]
[ letters
]
, "Text.View"
, equalsButFunctions
)
let view (model : Model) (dispatch : Dispatch<Msg>) =
textView
{
Model = model
Dispatch = dispatch
}
let init () =
let cmds =
Seq.toList "TIME FLIES LIKE AN ARROW"
|> Seq.mapi (fun i c ->
let msg = Letter (i, string c, 0, 0)
Cmd.ofMsg msg
)
|> Cmd.batch
{ Letters = Map.empty }, cmds
// App
Program.mkProgram init update view
|> Program.withReactSynchronous "elmish-app"
|> Program.run
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<script src="__HOST__/libs/react.production.min.js"></script>
<script src="__HOST__/libs/react-dom.production.min.js"></script>
<link rel="stylesheet" href="__HOST__/libs/css/bulma.min.css" />
<link rel="stylesheet" href="__HOST__/libs/css/all.min.css" />
</head>
<body class="app-container">
<script type="text/javascript">
window.throttle = function (callback, wait, immediate) {
let timeout = null
let initialCall = true
return function () {
const callNow = immediate && initialCall
const next = () => {
callback.apply(this, arguments)
timeout = null
}
if (callNow) {
initialCall = false
next()
}
if (!timeout) {
timeout = setTimeout(next, wait)
}
}
}
</script>
<div id="elmish-app" class="elmish-app"></div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment