some module organization
This commit is contained in:
parent
d50eb26b14
commit
7de147dabb
52
Client.fs
52
Client.fs
@ -1,23 +1,59 @@
|
|||||||
namespace web_api_cookbook
|
namespace web_api_cookbook
|
||||||
|
|
||||||
|
open web_api_cookbook
|
||||||
|
open web_api_cookbook.Exercises
|
||||||
open WebSharper
|
open WebSharper
|
||||||
open WebSharper.UI
|
open WebSharper.UI
|
||||||
open WebSharper.UI.Client
|
open WebSharper.UI.Client
|
||||||
open WebSharper.UI.Html
|
open WebSharper.UI.Html
|
||||||
open Units.Animation
|
|
||||||
open Units.Time
|
|
||||||
|
|
||||||
|
[<JavaScript>]
|
||||||
|
module Exercise =
|
||||||
|
let private c = attr.``class``
|
||||||
|
let private showHide () =
|
||||||
|
let show = Var.Create false
|
||||||
|
let text =
|
||||||
|
function
|
||||||
|
| true -> "(Hide)"
|
||||||
|
| false -> "(Show)"
|
||||||
|
|> View.MapCached <| show.View
|
||||||
|
|
||||||
|
let toggle =
|
||||||
|
fun _ _ curr ->
|
||||||
|
Var.Set show (not curr)
|
||||||
|
|> on.clickView show.View
|
||||||
|
|
||||||
|
show.View, div [c "button button--text"; toggle] [textView text]
|
||||||
|
|
||||||
|
let doc title description content =
|
||||||
|
let expanded, showHide = showHide ()
|
||||||
|
div [c "exercise"] [
|
||||||
|
h2 [c "exercise__title"] [showHide; text title]
|
||||||
|
fun () ->
|
||||||
|
div [c "exercise__section"] [
|
||||||
|
div [c "exercise__description"] [text description]
|
||||||
|
div [c "exercise__content"] [content ()]
|
||||||
|
]
|
||||||
|
|> Doc.When expanded
|
||||||
|
]
|
||||||
|
|
||||||
[<JavaScript>]
|
[<JavaScript>]
|
||||||
module Client =
|
module Client =
|
||||||
[<SPAEntryPoint>]
|
[<SPAEntryPoint>]
|
||||||
let Main () =
|
let Main () =
|
||||||
let isClicked = Var.Create false
|
|
||||||
let opacityAnimated = Animate.valueWhen isClicked.View 1.0 0. 120.<frames/s> 1.<s>
|
|
||||||
let opacityStyle = View.MapCached (sprintf "opacity: %f") opacityAnimated
|
|
||||||
let onClick = on.click (fun _ _ -> Var.Set isClicked true)
|
|
||||||
|
|
||||||
div [] [
|
div [] [
|
||||||
UI.Components.button [attr.styleDyn opacityStyle; onClick] "Hide Me"
|
h1 [] [text "Using Various Browser Web APIs via WebSharper (in F#)"]
|
||||||
|
p [] [
|
||||||
|
text "WebSharper is a web framework that provides a functional reactive programming '-ish' API for use in web development."
|
||||||
|
text "It supports a variety of applicative and monadic combinators that work natuarally with F#, making it a pleasure to use."]
|
||||||
|
p [] [text "On this page I've implemented code snippets that use various features using the browser's Web API, using WebSharper. The source code can be viewed "; a [] [text "here."]]
|
||||||
|
Exercise.doc
|
||||||
|
"Using requestAnimationFrame"
|
||||||
|
"Trying to use Request Animation Frame"
|
||||||
|
RequestAnimationFrame.doc
|
||||||
|
Exercise.doc
|
||||||
|
"Syncing LocalStorage Across Tabs"
|
||||||
|
"Using LocalStorage to keep data synced between tabs."
|
||||||
|
LocalStorageSync.doc
|
||||||
]
|
]
|
||||||
|> Doc.RunById "main"
|
|> Doc.RunById "main"
|
||||||
|
|||||||
57
Exercises/LocalStorageSync.fs
Normal file
57
Exercises/LocalStorageSync.fs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
namespace web_api_cookbook.Exercises
|
||||||
|
open web_api_cookbook
|
||||||
|
open web_api_cookbook.UI
|
||||||
|
open web_api_cookbook.UI.Components
|
||||||
|
open WebSharper
|
||||||
|
open WebSharper.UI
|
||||||
|
open WebSharper.UI.Client
|
||||||
|
open WebSharper.UI.Html
|
||||||
|
|
||||||
|
[<JavaScript>]
|
||||||
|
module LocalStorageSync =
|
||||||
|
let doc () =
|
||||||
|
let resetKeyInput, keyInput, keyInputDoc = InputType.text [] "Key: "
|
||||||
|
let resetValInput, valInput, valInputDoc = InputType.text [] "Value: "
|
||||||
|
|
||||||
|
let args = View.Map2 tuple2 keyInput valInput
|
||||||
|
|
||||||
|
let clear () =
|
||||||
|
resetKeyInput ()
|
||||||
|
resetValInput ()
|
||||||
|
|
||||||
|
let onAdd =
|
||||||
|
fun _ _ (key, value) ->
|
||||||
|
LocalStorage.setItem key value
|
||||||
|
clear ()
|
||||||
|
|> on.clickView args
|
||||||
|
|
||||||
|
let addButton = Button.plain [onAdd] "Add Element"
|
||||||
|
|
||||||
|
let addedElements =
|
||||||
|
fun k element ->
|
||||||
|
let value = View.Map snd element
|
||||||
|
|
||||||
|
let onDelete =
|
||||||
|
fun _ _ ->
|
||||||
|
LocalStorage.removeItem k
|
||||||
|
|> on.click
|
||||||
|
|
||||||
|
Doc.Concat [
|
||||||
|
div [] [text <| sprintf "Key: %s" k]
|
||||||
|
div [] [textView <| View.Map (sprintf "Value: %s") value]
|
||||||
|
Button.plain [onDelete] "Delete Element"
|
||||||
|
]
|
||||||
|
|> Doc.BindSeqCachedViewBy fst <| LocalStorage.view ()
|
||||||
|
|
||||||
|
Doc.Concat [
|
||||||
|
div [] [text "Elements Added to Local Storage:"]
|
||||||
|
div [attr.``class`` "gridwrap"] [
|
||||||
|
addedElements
|
||||||
|
]
|
||||||
|
div [] [text "Add New Element:"]
|
||||||
|
div [attr.``class`` "gridwrap"] [
|
||||||
|
keyInputDoc
|
||||||
|
valInputDoc
|
||||||
|
addButton
|
||||||
|
]
|
||||||
|
]
|
||||||
23
Exercises/RequestAnimationFrame.fs
Normal file
23
Exercises/RequestAnimationFrame.fs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace web_api_cookbook.Exercises
|
||||||
|
|
||||||
|
open WebSharper
|
||||||
|
open WebSharper.UI
|
||||||
|
open WebSharper.UI.Client
|
||||||
|
open WebSharper.UI.Html
|
||||||
|
open web_api_cookbook.UI
|
||||||
|
open web_api_cookbook.Units.Animation
|
||||||
|
open web_api_cookbook.Units.Time
|
||||||
|
open web_api_cookbook.UI.Components
|
||||||
|
|
||||||
|
[<JavaScript>]
|
||||||
|
module RequestAnimationFrame =
|
||||||
|
let doc () =
|
||||||
|
let isClicked = Var.Create false
|
||||||
|
let opacityAnimated = Animate.valueWhen isClicked.View 1.0 0. 120.<frames/s> 1.<s>
|
||||||
|
let opacityStyle = View.MapCached (sprintf "opacity: %f") opacityAnimated
|
||||||
|
let animateOnClick =
|
||||||
|
fun _ _ ->
|
||||||
|
Var.Set isClicked true
|
||||||
|
|> on.click
|
||||||
|
|
||||||
|
Button.plain [attr.styleDyn opacityStyle; animateOnClick] "Hide Me"
|
||||||
16
Prelude.fs
16
Prelude.fs
@ -1,6 +1,8 @@
|
|||||||
namespace web_api_cookbook
|
namespace web_api_cookbook
|
||||||
|
|
||||||
open WebSharper
|
open WebSharper
|
||||||
|
open WebSharper.UI
|
||||||
|
open WebSharper.UI.Client
|
||||||
|
|
||||||
[<JavaScript>]
|
[<JavaScript>]
|
||||||
module Option =
|
module Option =
|
||||||
@ -20,6 +22,15 @@ module Units =
|
|||||||
[<Measure>]
|
[<Measure>]
|
||||||
type s
|
type s
|
||||||
|
|
||||||
|
[<JavaScript>]
|
||||||
|
module Doc =
|
||||||
|
let When show content =
|
||||||
|
function
|
||||||
|
| true -> content ()
|
||||||
|
| false -> Doc.Empty
|
||||||
|
|> View.MapCached <| show
|
||||||
|
|> Doc.EmbedView
|
||||||
|
|
||||||
[<JavaScript>]
|
[<JavaScript>]
|
||||||
module Math =
|
module Math =
|
||||||
let clamp a b c =
|
let clamp a b c =
|
||||||
@ -29,3 +40,8 @@ module Math =
|
|||||||
|
|
||||||
[<Inline>]
|
[<Inline>]
|
||||||
let inline lerp a b t = a + t * (b - a)
|
let inline lerp a b t = a + t * (b - a)
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
[<JavaScript>]
|
||||||
|
module Tuple2 =
|
||||||
|
let tuple2 a b = a,b
|
||||||
|
|||||||
@ -1,21 +1,15 @@
|
|||||||
namespace web_api_cookbook
|
namespace web_api_cookbook.UI
|
||||||
|
|
||||||
|
open web_api_cookbook
|
||||||
|
open web_api_cookbook.Units.Animation
|
||||||
|
open web_api_cookbook.Units.Time
|
||||||
open WebSharper
|
open WebSharper
|
||||||
open WebSharper.JavaScript
|
|
||||||
open WebSharper.UI
|
open WebSharper.UI
|
||||||
open WebSharper.UI.Client
|
open WebSharper.JavaScript
|
||||||
open WebSharper.UI.Html
|
|
||||||
|
|
||||||
[<JavaScript>]
|
|
||||||
module UI =
|
|
||||||
module Components =
|
|
||||||
let button attrs label =
|
|
||||||
button (attrs @ [attr.``type`` "button"]) [ text label ]
|
|
||||||
|
|
||||||
[<JavaScript>]
|
[<JavaScript>]
|
||||||
module Animate =
|
module Animate =
|
||||||
open Units.Animation
|
|
||||||
open Units.Time
|
|
||||||
let value (startPoint:float) endPoint (targetFps:float<frames/s>) (animationSeconds:float<s>) =
|
let value (startPoint:float) endPoint (targetFps:float<frames/s>) (animationSeconds:float<s>) =
|
||||||
let frameInterval = 1. / targetFps * 1000.0<ms/s>
|
let frameInterval = 1. / targetFps * 1000.0<ms/s>
|
||||||
let frameCount = animationSeconds * targetFps
|
let frameCount = animationSeconds * targetFps
|
||||||
23
UI/Components.fs
Normal file
23
UI/Components.fs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace web_api_cookbook.UI
|
||||||
|
|
||||||
|
open WebSharper
|
||||||
|
open WebSharper.JavaScript
|
||||||
|
open WebSharper.UI
|
||||||
|
open WebSharper.UI.Client
|
||||||
|
open WebSharper.UI.Html
|
||||||
|
|
||||||
|
[<JavaScript>]
|
||||||
|
module Components =
|
||||||
|
module Button =
|
||||||
|
let plain attrs label =
|
||||||
|
button (attrs @ [attr.``type`` "button"; attr.``class`` "button"]) [ text label ]
|
||||||
|
|
||||||
|
module InputType =
|
||||||
|
let reset var () =
|
||||||
|
Var.Set var ""
|
||||||
|
|
||||||
|
let text attrs label =
|
||||||
|
let inputVal = Var.Create ""
|
||||||
|
let input = Doc.InputType.Text attrs inputVal
|
||||||
|
let doc = span [] [text label; input]
|
||||||
|
reset inputVal, inputVal.View, doc
|
||||||
54
UI/LocalStorage.fs
Normal file
54
UI/LocalStorage.fs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
namespace web_api_cookbook.UI
|
||||||
|
|
||||||
|
open WebSharper
|
||||||
|
open WebSharper.UI
|
||||||
|
open WebSharper.JavaScript
|
||||||
|
|
||||||
|
[<JavaScript>]
|
||||||
|
module LocalStorage =
|
||||||
|
let allItems : Var<list<string * string>> = Var.Create []
|
||||||
|
|
||||||
|
let getItem (key: string) : option<string> =
|
||||||
|
match JS.Window.LocalStorage.GetItem key with
|
||||||
|
| null -> None
|
||||||
|
| value -> Some value
|
||||||
|
|
||||||
|
let private putItem (key: string) (value:string) : unit =
|
||||||
|
Var.Get allItems
|
||||||
|
|> List.filter (fun (k, _) -> k <> key)
|
||||||
|
|> fun filtered -> (key, value) :: filtered
|
||||||
|
|> Var.Set allItems
|
||||||
|
|
||||||
|
let private deleteItem (key: string) : unit =
|
||||||
|
Var.Get allItems
|
||||||
|
|> List.filter (fun (k, _) -> k <> key)
|
||||||
|
|> Var.Set allItems
|
||||||
|
|
||||||
|
let setItem (key: string) (value: string) : unit =
|
||||||
|
JS.Window.LocalStorage.SetItem(key, value)
|
||||||
|
putItem key value
|
||||||
|
|
||||||
|
let removeItem (key: string) : unit =
|
||||||
|
JS.Window.LocalStorage.RemoveItem(key)
|
||||||
|
deleteItem key
|
||||||
|
|
||||||
|
JS.Window.AddEventListener("storage", fun (e:Dom.Event) ->
|
||||||
|
let key:string option = e?key |> Option.ofObj
|
||||||
|
let newValue:string option = e?newValue |> Option.ofObj
|
||||||
|
Console.Info key
|
||||||
|
Console.Info newValue
|
||||||
|
match key, newValue with
|
||||||
|
| Some key, Some newVal -> putItem key newVal
|
||||||
|
| Some key, None -> removeItem key
|
||||||
|
| _ -> ())
|
||||||
|
|
||||||
|
let view () =
|
||||||
|
let items =
|
||||||
|
[
|
||||||
|
for i in 0 .. JS.Window.LocalStorage.Length - 1 do
|
||||||
|
let key = JS.Window.LocalStorage.Key i
|
||||||
|
let value = JS.Window.LocalStorage.GetItem key
|
||||||
|
yield (key, value)
|
||||||
|
]
|
||||||
|
allItems.Set items
|
||||||
|
allItems.View
|
||||||
@ -10,7 +10,11 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Prelude.fs" />
|
<Compile Include="Prelude.fs" />
|
||||||
<Compile Include="Ui.fs" />
|
<Compile Include="UI/LocalStorage.fs" />
|
||||||
|
<Compile Include="UI/Components.fs" />
|
||||||
|
<Compile Include="UI/Animate.fs" />
|
||||||
|
<Compile Include="Exercises/RequestAnimationFrame.fs" />
|
||||||
|
<Compile Include="Exercises/LocalStorageSync.fs" />
|
||||||
<Compile Include="Client.fs" />
|
<Compile Include="Client.fs" />
|
||||||
<Compile Include="Startup.fs" />
|
<Compile Include="Startup.fs" />
|
||||||
<None Include="package.json" />
|
<None Include="package.json" />
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<title>web_api_cookbook</title>
|
<title>web_api_cookbook</title>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="stylesheet" type="text/css" href="Scripts/web_api_cookbook.css" />
|
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||||
<script type="text/javascript" src="Scripts/web_api_cookbook.head.js"></script>
|
<script type="text/javascript" src="Scripts/web_api_cookbook.head.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
52
wwwroot/style.css
Normal file
52
wwwroot/style.css
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
h1 {
|
||||||
|
font-size: 1.2rem; /* or 28px */
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.1rem; /* or 22px */
|
||||||
|
margin-bottom: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gridwrap {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 15em);
|
||||||
|
}
|
||||||
|
|
||||||
|
.exercise {
|
||||||
|
margin_bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exercise__title {
|
||||||
|
display: flex;
|
||||||
|
gap: .5em;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exercise__section {
|
||||||
|
border-left: 2px solid lightgray;
|
||||||
|
padding-left: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exercise__description {
|
||||||
|
color: #666;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exercise__content {
|
||||||
|
margin-top: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--text {
|
||||||
|
font-style:italic;
|
||||||
|
color: blue
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user