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
|
||||
|
||||
open web_api_cookbook
|
||||
open web_api_cookbook.Exercises
|
||||
open WebSharper
|
||||
open WebSharper.UI
|
||||
open WebSharper.UI.Client
|
||||
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>]
|
||||
module Client =
|
||||
[<SPAEntryPoint>]
|
||||
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 [] [
|
||||
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"
|
||||
|
||||
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
|
||||
|
||||
open WebSharper
|
||||
open WebSharper.UI
|
||||
open WebSharper.UI.Client
|
||||
|
||||
[<JavaScript>]
|
||||
module Option =
|
||||
@ -20,6 +22,15 @@ module Units =
|
||||
[<Measure>]
|
||||
type s
|
||||
|
||||
[<JavaScript>]
|
||||
module Doc =
|
||||
let When show content =
|
||||
function
|
||||
| true -> content ()
|
||||
| false -> Doc.Empty
|
||||
|> View.MapCached <| show
|
||||
|> Doc.EmbedView
|
||||
|
||||
[<JavaScript>]
|
||||
module Math =
|
||||
let clamp a b c =
|
||||
@ -29,3 +40,8 @@ module Math =
|
||||
|
||||
[<Inline>]
|
||||
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.JavaScript
|
||||
open WebSharper.UI
|
||||
open WebSharper.UI.Client
|
||||
open WebSharper.UI.Html
|
||||
|
||||
[<JavaScript>]
|
||||
module UI =
|
||||
module Components =
|
||||
let button attrs label =
|
||||
button (attrs @ [attr.``type`` "button"]) [ text label ]
|
||||
open WebSharper.JavaScript
|
||||
|
||||
[<JavaScript>]
|
||||
module Animate =
|
||||
open Units.Animation
|
||||
open Units.Time
|
||||
|
||||
let value (startPoint:float) endPoint (targetFps:float<frames/s>) (animationSeconds:float<s>) =
|
||||
let frameInterval = 1. / targetFps * 1000.0<ms/s>
|
||||
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>
|
||||
<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="Startup.fs" />
|
||||
<None Include="package.json" />
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<title>web_api_cookbook</title>
|
||||
<meta charset="utf-8" />
|
||||
<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>
|
||||
</head>
|
||||
<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