Last active
September 21, 2017 15:55
-
-
Save DanCouper/0fc4b3fb335247447ed7cfc7db9ede07 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* --------------------------- | |
* Helpers | |
* --------------------------- */ | |
/* `ReasonReact.stringToElement` is a little verbose, so alias it. | |
* NOTE this particular vagary get annoying quickly. */ | |
let stringToEl = ReasonReact.stringToElement; | |
/* ReasonReact doesn't play nice with React Devtools. This is a pain | |
* point; for development, manually printing out the state turns out | |
* to be helpful: */ | |
let intListToReactString intList => | |
stringToEl (Helpers.intListToString intList); | |
/* --------------------------- | |
* SequenceDisplay component | |
* | |
* This, on mount, diplays a sequence of list items. | |
* NOTE: Initially, I tried to make this do too much - I included | |
* the controls within the component, and tried to hook into | |
* `didUpdate`, switching internal state. After a few failed | |
* attempts, I used something that's basically identical to | |
* the example in the guide. | |
* TODO: replace `setinterval` with a `requestAnimationFrame` | |
* based solution. Slight wierdness - Bs_WebIncubator has a | |
* binding for `requestAnimationFrame`, but not for | |
* `cancelAnimationFrame`. | |
* TODO: this has to notify the parent that it's finished. | |
* Need a callback passed as a prop. | |
* REVIEW: The state _could_ be dispensed with entirely, need to | |
* investigate. Because CSS props can be passed, a keyframe | |
* animation could be generated within the component based on | |
* passed args: the sequence is simple, and the timings linear. | |
* The end event could occur on an `onAnimationEnd` or | |
* `onTransitionEnd` callback. This idea has legs. | |
* --------------------------- */ | |
type action = | |
| Tick; | |
type state = { | |
displaySeq: list int, | |
timerId: ref (option Js.Global.intervalId) | |
}; | |
let sequence state sequenceCompleteNotifier => | |
switch state.displaySeq { | |
| [] => | |
ReasonReact.UpdateWithSideEffects | |
{...state, timerId: ref None} sequenceCompleteNotifier | |
| [x, ...xs] => ReasonReact.Update {...state, displaySeq: xs} | |
}; | |
let component = ReasonReact.reducerComponent "SequenceDisplay"; | |
let make ::displaySeq ::timeoutDelay=1000 ::displayEndNotifier _children => { | |
...component, | |
initialState: fun () => {displaySeq, timerId: ref None}, | |
reducer: fun action state => | |
switch action { | |
| Tick => sequence state displayEndNotifier | |
}, | |
didMount: fun self => { | |
self.state.timerId := | |
Some (Js.Global.setInterval (self.reduce (fun _ => Tick)) timeoutDelay); | |
ReasonReact.NoUpdate | |
}, | |
render: fun {state} => <p> (intListToReactString state.displaySeq) </p> | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let component = ReasonReact.statelessComponent "SequenceInput"; | |
let make ::inputs _children => { | |
...component, | |
render: fun self => | |
<figure> | |
( | |
ReasonReact.arrayToElement ( | |
Array.of_list ( | |
List.map | |
( | |
fun {keyValue} => | |
<button | |
value=(string_of_int keyValue) | |
key=(string_of_int keyValue)> | |
(ReasonReact.stringToElement (string_of_int keyValue)) | |
</button> | |
) | |
inputs | |
) | |
) | |
) | |
</figure> | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* --------------------------- | |
* Helpers | |
* --------------------------- */ | |
/* `ReasonReact.stringToElement` is a little verbose, so alias it. | |
* NOTE this particular vagary get annoying quickly. */ | |
let stringToEl = ReasonReact.stringToElement; | |
/* --------------------------- | |
* Simon component | |
* | |
* The core component, eventually should just have job of shifting between game states. | |
* --------------------------- */ | |
type gameState = | |
| Waiting | |
| Ready | |
| Displaying | |
| Displayed | |
| Playing; | |
let gameStateToReactStr gameState => | |
switch gameState { | |
| Waiting => stringToEl "Game state: Waiting" | |
| Ready => stringToEl "Game state: Ready" | |
| Displaying => stringToEl "Game state: Displaying" | |
| Displayed => stringToEl "Game state: Displayed" | |
| Playing => stringToEl "Game state: Playing" | |
}; | |
type action = | |
| Start | |
| Display | |
| DisplayComplete | |
| Play | |
| Check int; | |
type state = {gameState, seq: list int}; | |
let component = ReasonReact.reducerComponent "Simon"; | |
let make ::rounds ::keys _children => { | |
...component, | |
initialState: fun () => {gameState: Waiting, seq: []}, | |
reducer: fun action state => | |
switch action { | |
| Start => ReasonReact.Update {...state, gameState: Ready} | |
| Display => ReasonReact.Update {...state, gameState: Displaying} | |
| DisplayComplete => ReasonReact.Update {...state, gameState: Displayed} | |
| Play => ReasonReact.Update {...state, gameState: Playing} | |
| Check i => ReasonReact.NoUpdate | |
}, | |
render: fun self => | |
<figure> | |
<header> | |
/* DEBUG AREA */ | |
<p> (gameStateToReactStr self.state.gameState) </p> | |
</header> | |
( | |
switch self.state.gameState { | |
| Displaying => | |
<SequenceDisplay | |
displaySeq=[1, 2, 3, 4, 5, 6, 7] | |
timeoutDelay=500 | |
displayEndNotifier=(self.reduce (fun _ => DisplayComplete)) | |
/> | |
| Playing => | |
<SequenceInput | |
inputs=( | |
List.map | |
( | |
fun i => { | |
keyValue: i | |
/* inputCallback: self.reduce (fun _ => Check i) */ | |
} | |
) | |
self.state.displaySeq | |
) | |
/> | |
| _ => ReasonReact.nullElement | |
} | |
) | |
( | |
switch self.state.gameState { | |
| Waiting => | |
<button onClick=(self.reduce (fun _ => Start))> | |
(stringToEl "Start Round") | |
</button> | |
| Ready => | |
<button onClick=(self.reduce (fun _ => Display))> | |
(stringToEl "Display Sequence") | |
</button> | |
| Displaying => ReasonReact.nullElement | |
| Displayed => | |
<button onClick=(self.reduce (fun _ => Play))> | |
(stringToEl "Start Guessing") | |
</button> | |
| Playing => ReasonReact.nullElement | |
} | |
) | |
</figure> | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment