open CreateElement let render (config : Config.config) onStop = let duration = Config.getDuration config in (* State *) let interval = ref None in let elapsed = ref 0 in let step = ref (Step.getAt config !elapsed) in let isPlaying = ref true in (* Elements *) let stepElt = text (Step.prettyPrint !step.step) in let durationElt = text (Duration.prettyPrint !step.remaining) in let arcPathElt = path ~attributes:[| className "g-Timer__ArcProgress" |] () in let tabataCurrentElt = text (Js.Int.toString !step.tabata) in let cycleCurrentElt = text (Js.Int.toString !step.cycle) in (* Update *) let stop () = let () = Belt.Option.forEach !interval Js.Global.clearInterval in onStop config in let updateDom () = let angle = Js.Int.toFloat !elapsed /. Js.Int.toFloat duration *. 360.0 in let () = Element.setAttribute arcPathElt "d" (Arc.describe 0.0 0.0 95.0 0.0 angle) in let step = Step.getAt config !elapsed in let () = Element.setTextContent stepElt (Step.prettyPrint step.step) in let () = Element.setTextContent durationElt (Duration.prettyPrint step.remaining) in let () = Element.setTextContent tabataCurrentElt (Js.Int.toString step.tabata) in let () = Element.setTextContent cycleCurrentElt (Js.Int.toString step.cycle) in Audio.playFromStep config step in let update () = if !isPlaying then let () = elapsed := !elapsed + 1 in let () = step := Step.getAt config !elapsed in if !elapsed > duration then stop () else updateDom () else () in (* Start timer *) let () = interval := Some (Js.Global.setInterval update 1000) in (* View *) section ~attributes:[| className "g-Timer" |] ~children: [| button ~attributes:[| className "g-Timer__Dial" |] ~eventListeners:[| onClick (fun _ -> isPlaying := not !isPlaying) |] ~children: [| svg ~attributes: [| className "g-Timer__Arc"; viewBox "-100 -100 200 200" |] ~children: [| path ~attributes: [| className "g-Timer__ArcTotal"; d (Arc.describe 0.0 0.0 95.0 0.0 359.999); |] (); arcPathElt; |] (); div ~attributes:[| className "g-Timer__Step" |] ~children:[| stepElt |] (); div ~attributes:[| className "g-Timer__Duration" |] ~children:[| durationElt |] (); |] (); div ~attributes:[| className "g-Timer__TabataAndCycle" |] ~children: [| div ~attributes:[| className "g-Timer__Tabata" |] ~children: [| div ~children:[| text "Tabata" |] (); span ~attributes:[| className "g-Timer__TabataCurrent" |] ~children:[| tabataCurrentElt |] (); text "/"; span ~attributes:[| className "g-Timer__TabataTotal" |] ~children:[| text (Js.Int.toString config.tabatas) |] (); |] (); div ~attributes:[| className "g-Timer__Cycle" |] ~children: [| div ~children:[| text "Cycle" |] (); span ~attributes:[| className "g-Timer__CycleCurrent" |] ~children:[| cycleCurrentElt |] (); text "/"; span ~attributes:[| className "g-Timer__CycleTotal" |] ~children:[| text (Js.Int.toString config.cycles) |] (); |] (); |] (); div ~attributes:[| className "g-Timer__Stop" |] ~children:[| text "stop" |] ~eventListeners:[| onClick (fun _ -> stop ()) |] (); |] ()