Skip to content

Commit

Permalink
Port all Halogen examples to Halogen Hooks recipes (#177)
Browse files Browse the repository at this point in the history
* Add ComponentsInputHalogenHooks

* Add ComponentsMultiTypeHalogenHooks

* Add DriverIoHalogenHooks

* Remove unused dependency: halogen-hooks-extra

* Add DriverRoutingHalogenHooks

* Add DriverWebSocketsHalogenHooks

* Add InterpretHalogenHooks

* Add KeyboardInputHalogenHooks

* Add LifecycleHalogenHooks

* Fix compiler warning about shadowed variable name

* Import constructors; remove unused import
  • Loading branch information
JordanMartinez authored Jul 15, 2020
1 parent 0b49a62 commit 4e21666
Show file tree
Hide file tree
Showing 49 changed files with 1,064 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,15 @@ Running a web-compatible recipe:
| | :heavy_check_mark: | [CatGifsHalogenHooks](recipes/CatGifsHalogenHooks) | A Halogen port of the ["HTTP - Cat GIFs" Elm Example](https://elm-lang.org/examples/cat-gifs). |
| | :heavy_check_mark: | [CatGifsReactHooks](recipes/CatGifsReactHooks) | A React port of the ["HTTP - Cat GIFs" Elm Example](https://elm-lang.org/examples/cat-gifs). |
| | :heavy_check_mark: | [ComponentsHalogenHooks](recipes/ComponentsHalogenHooks) | Demonstrates how to nest one Halogen-Hooks-based component inside another and send/receive queries between the two. |
| | :heavy_check_mark: | [ComponentsInputHalogenHooks](recipes/ComponentsInputHalogenHooks) | Each time a parent re-renders, it will pass a new input value into the child, and the child will update accordingly. |
| | :heavy_check_mark: | [ComponentsMultiTypeHalogenHooks](recipes/ComponentsMultiTypeHalogenHooks) | Demonstrates a component that can communicate with its children that have differing types. |
| :heavy_check_mark: | :heavy_check_mark: | [DebuggingLog](recipes/DebuggingLog) | This recipe shows how to do print-debugging using the `Debug` module's `spy` and `traceM` functions. The compiler will emit warnings to remind you to remove these debug functions before you ship production code. |
| :heavy_check_mark: | | [DiceCLI](recipes/DiceCLI) | This recipe shows how to create an interactive command line prompt that repeatedly generates a random number between 1 and 6. |
| :heavy_check_mark: | :heavy_check_mark: | [DiceLog](recipes/DiceLog) | This recipe shows how to log a random integer between 1 and 6 (representing a roll of a die) in either the node.js or web browser console. |
| | :heavy_check_mark: | [DragAndDropHalogenHooks](recipes/DragAndDropHalogenHooks) | A Halogen port of the ["Files - Drag-and-Drop" Elm Example](https://elm-lang.org/examples/drag-and-drop). |
| | :heavy_check_mark: | [DriverIoHalogenHooks](recipes/DriverIoHalogenHooks) | Demonstrates how to communicate with a Halogen application by sending messages to and receiving messages from the root-level component via the driver. |
| | :heavy_check_mark: | [DriverRoutingHalogenHooks](recipes/DriverRoutingHalogenHooks) | Demonstrates using `hashchange` events to drive the root component in a Halogen application via the driver. |
| | :heavy_check_mark: | [DriverWebSocketsHalogenHooks](recipes/DriverWebSocketsHalogenHooks) | Demonstrates using a WebSocket to drive the main component in a Halogen application. |
| | :heavy_check_mark: | [FileUploadHalogenHooks](recipes/FileUploadHalogenHooks) | A Halogen port of the ["Files - Upload" Elm Example](https://elm-lang.org/examples/upload). |
| | :heavy_check_mark: | [FindDomElementJs](recipes/FindDomElementJs) | This recipe shows how to find elements in the DOM by using query selectors. |
| | :heavy_check_mark: | [FormsReactHooks](recipes/FormsReactHooks) | A React port of the ["User Interface - Forms" Elm Example](https://elm-lang.org/examples/forms). |
Expand All @@ -110,6 +115,9 @@ Running a web-compatible recipe:
| :heavy_check_mark: | :heavy_check_mark: | [HelloWorldLog](recipes/HelloWorldLog) | This recipe shows how to run a simple "Hello world!" program in either the node.js or web browser console. |
| :heavy_check_mark: | :heavy_check_mark: | [HeterogenousArrayLog](recipes/HeterogenousArrayLog) | This recipe demonstrates how to create a heterogenous array and process its elements. |
| | :heavy_check_mark: | [ImagePreviewsHalogenHooks](recipes/ImagePreviewsHalogenHooks) | A Halogen port of the ["Files - Drag-and-Drop" Elm Example](https://elm-lang.org/examples/drag-and-drop). |
| | :heavy_check_mark: | [InterpretHalogenHooks](recipes/InterpretHalogenHooks) | Demonstrates how to use a custom monad (in this case, using `ReaderT` with `Aff` as the effect type) for a component, and then interpreting that custom monad back down to `Aff`, so it can be run as a normal component. |
| | :heavy_check_mark: | [KeyboardInputHalogenHooks](recipes/KeyboardInputHalogenHooks) | This example demonstrates how to selectively capture keyboard events and, more generally, how to use `EventSource`s in Halogen. |
| | :heavy_check_mark: | [LifecycleHalogenHooks](recipes/LifecycleHalogenHooks) | Demonstrates component lifecycle. |
| | :heavy_check_mark: | [NumbersHalogenHooks](recipes/NumbersHalogenHooks) | A Halogen port of the ["Random - Numbers" Elm Example](https://elm-lang.org/examples/numbers). |
| | :heavy_check_mark: | [NumbersReactHooks](recipes/NumbersReactHooks) | A React port of the ["Random - Numbers" Elm Example](https://elm-lang.org/examples/numbers). |
| | :heavy_check_mark: | [PositionsHalogenHooks](recipes/PositionsHalogenHooks) | A Halogen port of the ["Random - Positions" Elm Example](https://elm-lang.org/examples/positions). |
Expand Down
13 changes: 13 additions & 0 deletions recipes/ComponentsInputHalogenHooks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/bower_components/
/node_modules/
/.pulp-cache/
/output/
/generated-docs/
/.psc-package/
/.psc*
/.purs*
/.psa*
/.spago
/web-dist/
/prod-dist/
/prod/
9 changes: 9 additions & 0 deletions recipes/ComponentsInputHalogenHooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# ComponentsInputHalogenHooks

Each time a parent re-renders, it will pass a new input value into the child, and the child will update accordingly.

## Expected Behavior:

### Browser

The parent stores an `Int`. Clicking the buttons will increment/decrement that value. The children will produce the result of a mathematical computation using that value.
10 changes: 10 additions & 0 deletions recipes/ComponentsInputHalogenHooks/spago.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{ name = "ComponentsInputHalogenHooks"
, dependencies =
[ "console"
, "effect"
, "halogen-hooks"
, "psci-support"
]
, packages = ../../packages.dhall
, sources = [ "recipes/ComponentsInputHalogenHooks/src/**/*.purs" ]
}
60 changes: 60 additions & 0 deletions recipes/ComponentsInputHalogenHooks/src/Main.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module ComponentsInputHalogenHooks.Main where

import Prelude hiding (top)

import Data.Maybe (Maybe(..))
import Data.Symbol (SProxy(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Class (class MonadEffect)
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.Hooks as Hooks
import Halogen.VDom.Driver (runUI)

main :: Effect Unit
main =
HA.runHalogenAff do
body <- HA.awaitBody
void $ runUI containerComponent unit body

_button :: SProxy "button"
_button = SProxy

containerComponent
:: forall unusedQuery unusedInput unusedOutput anyMonad
. MonadEffect anyMonad
=> H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad
containerComponent = Hooks.component \_ _ -> Hooks.do
state /\ stateIdx <- Hooks.useState 0
Hooks.pure $
HH.div_
[ HH.ul_
[ HH.slot _display 1 displayComponent state absurd
, HH.slot _display 2 displayComponent (state * 2) absurd
, HH.slot _display 3 displayComponent (state * 3) absurd
, HH.slot _display 4 displayComponent (state * 10) absurd
, HH.slot _display 5 displayComponent (state * state) absurd
]
, HH.button
[ HE.onClick \_ -> Just $ Hooks.modify_ stateIdx (_ + 1) ]
[ HH.text "+1"]
, HH.button
[ HE.onClick \_ -> Just $ Hooks.modify_ stateIdx (_ - 1) ]
[ HH.text "-1"]
]

_display = SProxy :: SProxy "display"

displayComponent
:: forall unusedQuery unusedOutput anyMonad
. MonadEffect anyMonad
=> H.Component HH.HTML unusedQuery Int unusedOutput anyMonad
displayComponent = Hooks.component \_ input -> Hooks.do
Hooks.pure $
HH.div_
[ HH.text "My input value is:"
, HH.strong_ [ HH.text (show input) ]
]
11 changes: 11 additions & 0 deletions recipes/ComponentsInputHalogenHooks/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ComponentsInputHalogenHooks</title>
</head>

<body>
<script src="./index.js"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions recipes/ComponentsInputHalogenHooks/web/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"use strict";
require("../../../output/ComponentsInputHalogenHooks.Main/index.js").main();
13 changes: 13 additions & 0 deletions recipes/ComponentsMultiTypeHalogenHooks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/bower_components/
/node_modules/
/.pulp-cache/
/output/
/generated-docs/
/.psc-package/
/.psc*
/.purs*
/.psa*
/.spago
/web-dist/
/prod-dist/
/prod/
9 changes: 9 additions & 0 deletions recipes/ComponentsMultiTypeHalogenHooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# ComponentsMultiTypeHalogenHooks

Demonstrates a component that can communicate with its children that have differing types.

## Expected Behavior:

### Browser

A toggle button, a count button, and an input field are the children of the parent. The user can modify either of those children's values and then click the "Check now" button. The parent will get the latest state from each of its children and display all states.
10 changes: 10 additions & 0 deletions recipes/ComponentsMultiTypeHalogenHooks/spago.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{ name = "ComponentsMultiTypeHalogenHooks"
, dependencies =
[ "console"
, "effect"
, "halogen-hooks"
, "psci-support"
]
, packages = ../../packages.dhall
, sources = [ "recipes/ComponentsMultiTypeHalogenHooks/src/**/*.purs" ]
}
130 changes: 130 additions & 0 deletions recipes/ComponentsMultiTypeHalogenHooks/src/Main.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
module ComponentsMultiTypeHalogenHooks.Main where

import Prelude

import Data.Maybe (Maybe(..))
import Data.Symbol (SProxy(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP
import Halogen.Hooks as Hooks
import Halogen.VDom.Driver (runUI)

main :: Effect Unit
main =
HA.runHalogenAff do
body <- HA.awaitBody
void $ runUI containerComponent unit body

_button :: SProxy "button"
_button = SProxy

containerComponent
:: forall unusedQuery unusedInput unusedOutput anyMonad
. H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad
containerComponent = Hooks.component \rec _ -> Hooks.do
state /\ stateIdx <- Hooks.useState { a: Nothing, b: Nothing, c: Nothing }
let
_a = SProxy :: SProxy "a"
_b = SProxy :: SProxy "b"
_c = SProxy :: SProxy "c"

Hooks.pure $
HH.div_
[ HH.div
[ HP.class_ (H.ClassName "box")]
[ HH.h1_ [ HH.text "Component A" ]
, HH.slot _a unit componentA unit absurd
]
, HH.div
[ HP.class_ (H.ClassName "box")]
[ HH.h1_ [ HH.text "Component B" ]
, HH.slot _b unit componentB unit absurd
]
, HH.div
[ HP.class_ (H.ClassName "box")]
[ HH.h1_ [ HH.text "Component C" ]
, HH.slot _c unit componentC unit absurd
]
, HH.p_
[ HH.text "Last observed states:"]
, HH.ul_
[ HH.li_ [ HH.text ("Component A: " <> show state.a) ]
, HH.li_ [ HH.text ("Component B: " <> show state.b) ]
, HH.li_ [ HH.text ("Component C: " <> show state.c) ]
]
, HH.button
[ HE.onClick \_ -> Just do
a <- Hooks.query rec.slotToken _a unit (H.request IsOn)
b <- Hooks.query rec.slotToken _b unit (H.request GetCount)
c <- Hooks.query rec.slotToken _c unit (H.request GetValue)
Hooks.put stateIdx { a, b, c }
]
[ HH.text "Check states now" ]
]

data QueryA a = IsOn (Boolean -> a)

componentA
:: forall unusedInput unusedOutput anyMonad
. H.Component HH.HTML QueryA unusedInput unusedOutput anyMonad
componentA = Hooks.component \rec _ -> Hooks.do
enabled /\ enabledIdx <- Hooks.useState false
Hooks.useQuery rec.queryToken case _ of
IsOn reply -> do
isEnabled <- Hooks.get enabledIdx
pure $ Just $ reply isEnabled
Hooks.pure $
HH.div_
[ HH.p_ [ HH.text "Toggle me!" ]
, HH.button
[ HE.onClick \_ -> Just do
Hooks.modify_ enabledIdx not ]
[ HH.text (if enabled then "On" else "Off") ]
]

data QueryB a = GetCount (Int -> a)

componentB
:: forall unusedInput unusedOutput anyMonad
. H.Component HH.HTML QueryB unusedInput unusedOutput anyMonad
componentB = Hooks.component \rec _ -> Hooks.do
count /\ countIdx <- Hooks.useState 0
Hooks.useQuery rec.queryToken case _ of
GetCount reply -> do
currentCount <- Hooks.get countIdx
pure $ Just $ reply currentCount
Hooks.pure $
HH.div_
[ HH.p_
[ HH.text "Current value: "
, HH.strong_ [ HH.text (show count) ]
]
, HH.button
[ HE.onClick \_ -> Just $ Hooks.modify_ countIdx (_ + 1) ]
[ HH.text ("Increment") ]
]

data QueryC a = GetValue (String -> a)

componentC
:: forall unusedInput unusedOutput anyMonad
. H.Component HH.HTML QueryC unusedInput unusedOutput anyMonad
componentC = Hooks.component \rec _ -> Hooks.do
state /\ stateIdx <- Hooks.useState "Hello"
Hooks.useQuery rec.queryToken case _ of
GetValue reply -> do
value <- Hooks.get stateIdx
pure $ Just $ reply value
Hooks.pure $
HH.label_
[ HH.p_ [ HH.text "What do you have to say?" ]
, HH.input
[ HP.value state
, HE.onValueInput (Just <<< Hooks.put stateIdx)
]
]
11 changes: 11 additions & 0 deletions recipes/ComponentsMultiTypeHalogenHooks/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ComponentsMultiTypeHalogenHooks</title>
</head>

<body>
<script src="./index.js"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions recipes/ComponentsMultiTypeHalogenHooks/web/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"use strict";
require("../../../output/ComponentsMultiTypeHalogenHooks.Main/index.js").main();
13 changes: 13 additions & 0 deletions recipes/DriverIoHalogenHooks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/bower_components/
/node_modules/
/.pulp-cache/
/output/
/generated-docs/
/.psc-package/
/.psc*
/.purs*
/.psa*
/.spago
/web-dist/
/prod-dist/
/prod/
11 changes: 11 additions & 0 deletions recipes/DriverIoHalogenHooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# DriverIoHalogenHooks

Demonstrates how to communicate with a Halogen application by sending messages to and receiving messages from the root-level component via the driver.

## Expected Behavior:

Prints:
```
The button state is currently: (Just false)
The button state is now: (Just true)
```
10 changes: 10 additions & 0 deletions recipes/DriverIoHalogenHooks/spago.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{ name = "DriverIoHalogenHooks"
, dependencies =
[ "console"
, "effect"
, "halogen-hooks"
, "psci-support"
]
, packages = ../../packages.dhall
, sources = [ "recipes/DriverIoHalogenHooks/src/**/*.purs" ]
}
Loading

0 comments on commit 4e21666

Please sign in to comment.