diff --git a/frontend/src/Frontend/Examples/BasicToDo/Main.hs b/frontend/src/Frontend/Examples/BasicToDo/Main.hs index 2e87691..b55209c 100644 --- a/frontend/src/Frontend/Examples/BasicToDo/Main.hs +++ b/frontend/src/Frontend/Examples/BasicToDo/Main.hs @@ -31,9 +31,10 @@ new v m = case M.maxViewWithKey m of ulW :: (DomBuilder t m, PostBuild t m, MonadFix m, MonadHold t m) => Dynamic t (MM T.Text) -> m (Dynamic t (MM (Event t Int))) ulW xs = elClass "ul" "list" $ listWithKey xs $ \k x -> elClass "li" "element" $ do - dynText x -- output the text - fmap (const k) <$> elClass "div" "delete" (button "x") + e <- fmap (const k) <$> elClass "span" "delete" (button "x") -- tag the event of button press with the key of the text + elAttr "span" ("style" =: "padding: 0.5em;") $ dynText x -- output the text + return e -- output an input text widget with auto clean on return and return an -- event firing on return containing the string before clean @@ -67,4 +68,6 @@ listW e = do return () app :: (DomBuilder t m, PostBuild t m, MonadFix m, MonadHold t m) => m () -app = el "div" $ inputW >>= listW +app = el "div" $ do + el "h4" $ text "ToDo List" + inputW >>= listW diff --git a/frontend/src/Frontend/Examples/DragAndDrop/Main.hs b/frontend/src/Frontend/Examples/DragAndDrop/Main.hs index 1d10cb5..1062b7b 100644 --- a/frontend/src/Frontend/Examples/DragAndDrop/Main.hs +++ b/frontend/src/Frontend/Examples/DragAndDrop/Main.hs @@ -21,13 +21,8 @@ import qualified GHCJS.DOM.GlobalEventHandlers as DOM import qualified GHCJS.DOM.MouseEvent as DOM import qualified GHCJS.DOM.Types as DOM (uncheckedCastTo) -import Language.Javascript.JSaddle -import Reflex.Dom hiding (mainWidget) -import Reflex.Dom.Core (mainWidget) -import Obelisk.Generated.Static - -main :: IO () -main = run $ mainWidget app +import Language.Javascript.JSaddle +import Reflex.Dom.Core app :: ( DomBuilder t m @@ -35,19 +30,23 @@ app ) => m () app = do - prerender_ blank $ do - draggable item1 "a picture" - draggable item2 "some code" + el "h3" $ text "Drag and Drop" + el "div" $ prerender_ blank $ draggable item1 "Haskell logo" + el "div" $ prerender_ blank $ draggable item2 "some text" + text "Drap either the above logo or the text to the below box" handleDragEvents return () + item1 :: DomBuilder t m => m (Element EventResult (DomBuilderSpace m) t, ()) -item1 = elAttr' "img" ("draggable" =: "true" <> "src" =: static @"obelisk.jpg") blank +item1 = elAttr' "img" ("draggable" =: "true" <> "src" =: haskellLogoUrl) blank + where + haskellLogoUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Haskell-Logo.svg/180px-Haskell-Logo.svg.png" item2 :: DomBuilder t m => m (Element EventResult (DomBuilderSpace m) t, ()) item2 = elAttr' "pre" ("draggable" =: "true" <> "style" =: "-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;") - $ text "main = putStrLn \"Hello world!\"" + $ text "Draggable text" draggable :: ( DomBuilder t m diff --git a/frontend/src/Frontend/Examples/TicTacToe/Main.hs b/frontend/src/Frontend/Examples/TicTacToe/Main.hs index 1556ef3..f5c0c82 100644 --- a/frontend/src/Frontend/Examples/TicTacToe/Main.hs +++ b/frontend/src/Frontend/Examples/TicTacToe/Main.hs @@ -111,8 +111,10 @@ gameBoard gameBoard squares = divClass "game-board" $ do evs <- forM [minBound .. maxBound] $ \row -> divClass "board-row" $ do evs <- forM [minBound .. maxBound] $ \col -> do - let square = (row, col) - (btn1,_) <- elAttr' "button" ("class" =: "square") $ + let + square = (row, col) + style = "height: 2em; width: 2em; position: block;" + (btn1,_) <- elAttr' "button" ("style" =: style) $ dynText (getSquareText square <$> squares) return (square <$ domEvent Click btn1) return (leftmost evs) @@ -121,7 +123,7 @@ gameBoard squares = divClass "game-board" $ do -- | What to display on square getSquareText :: (Row, Col) -> Squares -> Text getSquareText square squares = case squares A.! square of - Nothing -> "_" + Nothing -> "." (Just Player_X) -> "X" (Just Player_O) -> "O" diff --git a/frontend/src/Frontend/Examples/WebSocketEcho/Main.hs b/frontend/src/Frontend/Examples/WebSocketEcho/Main.hs index 8b272c5..5a20b92 100644 --- a/frontend/src/Frontend/Examples/WebSocketEcho/Main.hs +++ b/frontend/src/Frontend/Examples/WebSocketEcho/Main.hs @@ -4,15 +4,11 @@ module Frontend.Examples.WebSocketEcho.Main where -import Control.Monad (join) -import Data.Text.Encoding (encodeUtf8, decodeUtf8) -import Reflex.Dom hiding (mainWidget) -import Reflex.Dom.Core (mainWidget) +import Data.Text.Encoding (encodeUtf8, decodeUtf8) +import Reflex.Dom +import Control.Monad (join) import Control.Monad.Fix (MonadFix) -main :: IO () -main = run $ mainWidget app - app :: ( DomBuilder t m , MonadFix m diff --git a/reflex-dom-projects/generateAndTestExamples.hs b/reflex-dom-projects/generateAndTestExamples.hs new file mode 100755 index 0000000..8386d60 --- /dev/null +++ b/reflex-dom-projects/generateAndTestExamples.hs @@ -0,0 +1,44 @@ +#! /usr/bin/env nix-shell +#! nix-shell -i runghc -p "ghc.withPackages (pkgs: [ pkgs.shelly ])" + +{-# LANGUAGE OverloadedStrings #-} +-- | This script creates cabal projects from the template for individual examples + +import Shelly +import Control.Monad +import qualified Data.Text as T + +examples = + [ "BasicToDo" + , "DragAndDrop" + , "FileReader" + , "NasaPod" + , "ScreenKeyboard" + , "TicTacToe" + , "WebSocketEcho" + ] + +main :: IO () +main = shelly $ verbosely $ do + forM_ examples $ \exampleName -> do + -- Create the cabal project skeleton + let + genDir = fromText "generated" + templateDir = fromText "template" + targetDir = genDir (fromText exampleName) + rm_rf targetDir >> mkdir_p targetDir + cp_r templateDir genDir + mv (genDir templateDir (fromText "")) targetDir + mv (targetDir (fromText "template.cabal")) (targetDir (fromText exampleName <.> "cabal")) + files <- run "find" [(toTextIgnore targetDir), "-type", "f"] + forM_ (T.words files) $ \f -> do + let sedRegex = "s/EXAMPLENAME/" <> exampleName <> "/g" + run_ "sed" ["-i", "-e", sedRegex, f] + -- Copy the source code + let + prefixDir = (fromText "Frontend/Examples") + srcDir = (fromText "../frontend/src/Frontend/Examples") (fromText exampleName) + mkdir_p (targetDir prefixDir) + cp_r srcDir (targetDir prefixDir) + + -- TODO - Test cabal build diff --git a/reflex-dom-projects/generated/BasicToDo/BasicToDo.cabal b/reflex-dom-projects/generated/BasicToDo/BasicToDo.cabal new file mode 100644 index 0000000..cd88246 --- /dev/null +++ b/reflex-dom-projects/generated/BasicToDo/BasicToDo.cabal @@ -0,0 +1,37 @@ +name: reflex-examples-BasicToDo +version: 0.1.0.0 +synopsis: Examples for reflex and reflex-dom +-- description: +homepage: https://github.com/reflex-frp/reflex-examples +license: BSD3 +license-file: LICENSE +author: Divam +maintainer: dfordivam@gmail.com +-- copyright: +category: Web +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +executable reflex-examples-BasicToDo + main-is: Main.hs + other-modules: Frontend.Examples.BasicToDo.Main + -- other-extensions: + build-depends: base >=4.11 && <4.12 + , bytestring + , containers + , ghcjs-dom + , jsaddle + , lens + , modern-uri + , reflex + , reflex-dom + , random + , time + , vector + , unordered-containers + , text + , aeson + , array + -- , jsaddle-warp + default-language: Haskell2010 diff --git a/reflex-dom-projects/generated/BasicToDo/Frontend/Examples/BasicToDo/Main.hs b/reflex-dom-projects/generated/BasicToDo/Frontend/Examples/BasicToDo/Main.hs new file mode 100644 index 0000000..b55209c --- /dev/null +++ b/reflex-dom-projects/generated/BasicToDo/Frontend/Examples/BasicToDo/Main.hs @@ -0,0 +1,73 @@ +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecursiveDo #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Frontend.Examples.BasicToDo.Main + (app) + where + +{- + - Stripped version of todo list: just add new todo and delete an old one + -} + +import Control.Lens +import qualified Data.Map as M +import qualified Data.Text as T +import Reflex.Dom +import Control.Monad.Fix (MonadFix) + + +type MM a = M.Map Int a + +-- add a new value to a map, automatically choosing an unused key +new :: a -> MM a -> MM a +new v m = case M.maxViewWithKey m of + Nothing -> [(0,v)] -- overloadedlists + Just ((k, _), _) -> M.insert (succ k) v m + +-- output the ul of the elements of the given map and return the delete +-- event for each key +ulW :: (DomBuilder t m, PostBuild t m, MonadFix m, MonadHold t m) + => Dynamic t (MM T.Text) -> m (Dynamic t (MM (Event t Int))) +ulW xs = elClass "ul" "list" $ listWithKey xs $ \k x -> elClass "li" "element" $ do + e <- fmap (const k) <$> elClass "span" "delete" (button "x") + -- tag the event of button press with the key of the text + elAttr "span" ("style" =: "padding: 0.5em;") $ dynText x -- output the text + return e + +-- output an input text widget with auto clean on return and return an +-- event firing on return containing the string before clean +inputW :: (DomBuilder t m, MonadFix m) => m (Event t T.Text) +inputW = do + rec + let send = keypress Enter input + -- send signal firing on *return* key press + input <- inputElement $ def + & inputElementConfig_setValue .~ fmap (const "") send + & inputElementConfig_elementConfig . elementConfig_initialAttributes .~ + ("placeholder" =: "Write task and press enter") + -- inputElement with content reset on send + return $ tag (current $ _inputElement_value input) send + -- tag the send signal with the inputText value BEFORE resetting + +-- circuit ulW with a MM String kept updated by new strings from the passed +-- event and deletion of single element in the MM +listW :: (DomBuilder t m, PostBuild t m, MonadFix m, MonadHold t m) + => Event t T.Text -> m () +listW e = do + rec + xs <- foldDyn ($) M.empty $ mergeWith (.) + -- live state, updated by two signals + [ fmap new e -- insert a new text + , switch . current $ zs -- delete text at specific keys + ] + bs <- ulW xs -- delete signals from outputted state + let zs = fmap (mergeWith (.) . map (fmap M.delete) . M.elems) bs + -- merge delete events + return () + +app :: (DomBuilder t m, PostBuild t m, MonadFix m, MonadHold t m) => m () +app = el "div" $ do + el "h4" $ text "ToDo List" + inputW >>= listW diff --git a/reflex-dom-projects/generated/BasicToDo/Main.hs b/reflex-dom-projects/generated/BasicToDo/Main.hs new file mode 100644 index 0000000..0473774 --- /dev/null +++ b/reflex-dom-projects/generated/BasicToDo/Main.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Frontend.Examples.BasicToDo.Main +import Reflex.Dom +import Data.Map (Map) +import Data.Text (Text) +import qualified Data.Text as T + +main :: IO () +main = mainWidgetWithHead pageHead $ do + article $ app + +-- | An @
@ tag that will set its title and the class of its child +-- @
@ based on the current route +article + :: ( DomBuilder t m + , PostBuild t m + ) + => m () -- ^ Article content widget + -> m () +article c = el "main" $ el "article" c + +pageHead :: DomBuilder t m => m () +pageHead = do + el "title" $ text "Reflex FRP Examples" + elAttr "meta" metaDesc blank + elAttr "meta" metaKeywords blank + elAttr "meta" viewport blank + styleSheet $ "static/css/normalize.css" + styleSheet $ "static/css/fontawesome.min.css" + styleSheet $ "static/css/font.css" + styleSheet $ "static/css/style.css" + -- elAttr "script" ("type" =: "text/javascript" <> "src" =: static @"echarts.min.js") blank + +metaDesc :: Map Text Text +metaDesc = "name" =: "description" + <> "content" =: "Reflex Functional Reactive Programming Examples" + +metaKeywords :: Map Text Text +metaKeywords = "name" =: "keywords" + <> "content" =: "reflex, reflex frp, functional reactive programming, haskell, framework, reflex dom" + +viewport :: Map Text Text +viewport = "name" =: "viewport" + <> "content" =: "width=device-width, initial-scale=1" + +-- styleSheet are functions to add links to html +styleSheet :: DomBuilder t m => Text -> m () +styleSheet myLink = elAttr "link" attrs blank + where attrs = "rel" =: "stylesheet" + <> "type" =: "text/css" + <> "href" =: myLink + +tshow :: Show a => a -> Text +tshow = T.pack . show diff --git a/reflex-dom-projects/generated/BasicToDo/Setup.hs b/reflex-dom-projects/generated/BasicToDo/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/reflex-dom-projects/generated/BasicToDo/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/reflex-dom-projects/generated/DragAndDrop/DragAndDrop.cabal b/reflex-dom-projects/generated/DragAndDrop/DragAndDrop.cabal new file mode 100644 index 0000000..4793375 --- /dev/null +++ b/reflex-dom-projects/generated/DragAndDrop/DragAndDrop.cabal @@ -0,0 +1,37 @@ +name: reflex-examples-DragAndDrop +version: 0.1.0.0 +synopsis: Examples for reflex and reflex-dom +-- description: +homepage: https://github.com/reflex-frp/reflex-examples +license: BSD3 +license-file: LICENSE +author: Divam +maintainer: dfordivam@gmail.com +-- copyright: +category: Web +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +executable reflex-examples-DragAndDrop + main-is: Main.hs + other-modules: Frontend.Examples.DragAndDrop.Main + -- other-extensions: + build-depends: base >=4.11 && <4.12 + , bytestring + , containers + , ghcjs-dom + , jsaddle + , lens + , modern-uri + , reflex + , reflex-dom + , random + , time + , vector + , unordered-containers + , text + , aeson + , array + -- , jsaddle-warp + default-language: Haskell2010 diff --git a/reflex-dom-projects/generated/DragAndDrop/Frontend/Examples/DragAndDrop/Main.hs b/reflex-dom-projects/generated/DragAndDrop/Frontend/Examples/DragAndDrop/Main.hs new file mode 100644 index 0000000..c801fe3 --- /dev/null +++ b/reflex-dom-projects/generated/DragAndDrop/Frontend/Examples/DragAndDrop/Main.hs @@ -0,0 +1,127 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecursiveDo #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE PartialTypeSignatures #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE DataKinds #-} + +module Frontend.Examples.DragAndDrop.Main where + +import Data.Maybe (fromMaybe) +import Data.Monoid ((<>)) +import qualified GHCJS.DOM.DataTransfer as DOM +import qualified GHCJS.DOM.HTMLElement as DOM +import qualified GHCJS.DOM.Element as DOM +import qualified GHCJS.DOM.EventM as DOM +import qualified GHCJS.DOM.GlobalEventHandlers as DOM +import qualified GHCJS.DOM.MouseEvent as DOM +import qualified GHCJS.DOM.Types as DOM (uncheckedCastTo) + +import Language.Javascript.JSaddle +import Reflex.Dom.Core + +app + :: ( DomBuilder t m + , MonadHold t m + , PostBuild t m + , PerformEvent t m + , TriggerEvent t m + , Prerender js m + ) + => m () +app = do + el "h3" $ text "Drag and Drop" + el "div" $ draggable item1 "Haskell logo" + el "div" $ draggable item2 "some text" + text "Drap either the above logo or the text to the below box" + handleDragEvents + return () + + +item1 :: DomBuilder t m => m (Element EventResult (DomBuilderSpace m) t, ()) +item1 = elAttr' "img" ("draggable" =: "true" <> "src" =: haskellLogoUrl) blank + where + haskellLogoUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Haskell-Logo.svg/180px-Haskell-Logo.svg.png" + +item2 :: DomBuilder t m => m (Element EventResult (DomBuilderSpace m) t, ()) +item2 = elAttr' "pre" ("draggable" =: "true" + <> "style" =: "-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;") + $ text "Draggable text" + +draggable + :: ( DomBuilder t m + , TriggerEvent t m + , PerformEvent t m + , Prerender js m + ) + => m (Element EventResult (DomBuilderSpace m) t, ()) + -> String + -> m () +draggable elmnt attachment = do + dragsite <- fst <$> elmnt + prerender (return ()) $ do + dragStartEvent <- wrapDomEvent -- (_el_element dragsite) + -- (DOM.getToElement $ _element_raw dragsite) + (DOM.uncheckedCastTo DOM.HTMLElement $ _element_raw dragsite) + (`DOM.on` DOM.dragStart) $ do + dt <- fromMaybe (error "no dt?") + <$> (DOM.getDataTransfer =<< DOM.event) + DOM.setEffectAllowed dt ("all" :: JSString) + DOM.setDropEffect dt ("move" :: JSString) + DOM.setData dt + ("application/x-reflex-description" :: JSString) attachment + -- Bit of a hack here; this actually hooks the drag-start + -- event to the DOM, since otherwise nothing reflex-side + -- cares about the event + performEvent_ $ return () <$ dragStartEvent + return () + +handleDragEvents + :: ( DomBuilder t m + , TriggerEvent t m + , PostBuild t m + , MonadHold t m + , PerformEvent t m + , Prerender js m + ) + => m () +handleDragEvents = prerender (return ()) $ do + let + ddEvent :: (DOM.DataTransfer -> DOM.EventM e DOM.MouseEvent a) -> + DOM.EventM e DOM.MouseEvent a + ddEvent op = do + dt <- fromMaybe (error "no DT?") + <$> (DOM.getDataTransfer =<< DOM.event) + op dt + ddEvent_ :: DOM.EventM e DOM.MouseEvent () -> + DOM.EventM e DOM.MouseEvent () + ddEvent_ op = ddEvent (const op) + + rec + dragEnterEvent <- wrapDomEvent + dsHTMLel (`DOM.on` DOM.dragEnter) (ddEvent_ DOM.preventDefault) + dragLeaveEvent <- wrapDomEvent + dsHTMLel (`DOM.on` DOM.dragLeave) (ddEvent_ $ return ()) + dropsite <- fst <$> elDynAttr' "div" dropsiteAttrs (dynText dropText) + -- The following is defined to shorten the wrapDomEvent calls. + let dsHTMLel = DOM.uncheckedCastTo DOM.HTMLElement $ _element_raw dropsite + inDrop <- holdDyn False $ leftmost + [ True <$ dragEnterEvent + , False <$ dragLeaveEvent + , False <$ dropEvent] + dropsiteAttrs <- return . ffor inDrop $ \case + True -> "style" =: "border:1em solid blue;padding:2em;margin:2em;background-color:green;" + False -> "style" =: "border:1em solid blue;padding:2em;margin:2em;" + dragOverEvent <- wrapDomEvent + dsHTMLel (`DOM.on` DOM.dragOver) (ddEvent_ DOM.preventDefault) + performEvent_ $ return () <$ dragOverEvent + + dropEvent <- wrapDomEvent dsHTMLel (`DOM.on` DOM.drop) $ ddEvent $ \dt -> do + DOM.preventDefault + DOM.getData dt ("application/x-reflex-description" :: String) + dropText <- holdDyn "Drop here" $ fmap ("Dropped " <>) dropEvent + return () diff --git a/reflex-dom-projects/generated/DragAndDrop/Main.hs b/reflex-dom-projects/generated/DragAndDrop/Main.hs new file mode 100644 index 0000000..549e7f8 --- /dev/null +++ b/reflex-dom-projects/generated/DragAndDrop/Main.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Frontend.Examples.DragAndDrop.Main +import Reflex.Dom +import Data.Map (Map) +import Data.Text (Text) +import qualified Data.Text as T + +main :: IO () +main = mainWidgetWithHead pageHead $ do + article $ app + +-- | An @
@ tag that will set its title and the class of its child +-- @
@ based on the current route +article + :: ( DomBuilder t m + , PostBuild t m + ) + => m () -- ^ Article content widget + -> m () +article c = el "main" $ el "article" c + +pageHead :: DomBuilder t m => m () +pageHead = do + el "title" $ text "Reflex FRP Examples" + elAttr "meta" metaDesc blank + elAttr "meta" metaKeywords blank + elAttr "meta" viewport blank + styleSheet $ "static/css/normalize.css" + styleSheet $ "static/css/fontawesome.min.css" + styleSheet $ "static/css/font.css" + styleSheet $ "static/css/style.css" + -- elAttr "script" ("type" =: "text/javascript" <> "src" =: static @"echarts.min.js") blank + +metaDesc :: Map Text Text +metaDesc = "name" =: "description" + <> "content" =: "Reflex Functional Reactive Programming Examples" + +metaKeywords :: Map Text Text +metaKeywords = "name" =: "keywords" + <> "content" =: "reflex, reflex frp, functional reactive programming, haskell, framework, reflex dom" + +viewport :: Map Text Text +viewport = "name" =: "viewport" + <> "content" =: "width=device-width, initial-scale=1" + +-- styleSheet are functions to add links to html +styleSheet :: DomBuilder t m => Text -> m () +styleSheet myLink = elAttr "link" attrs blank + where attrs = "rel" =: "stylesheet" + <> "type" =: "text/css" + <> "href" =: myLink + +tshow :: Show a => a -> Text +tshow = T.pack . show diff --git a/reflex-dom-projects/generated/DragAndDrop/Setup.hs b/reflex-dom-projects/generated/DragAndDrop/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/reflex-dom-projects/generated/DragAndDrop/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/reflex-dom-projects/generated/FileReader/FileReader.cabal b/reflex-dom-projects/generated/FileReader/FileReader.cabal new file mode 100644 index 0000000..5208e93 --- /dev/null +++ b/reflex-dom-projects/generated/FileReader/FileReader.cabal @@ -0,0 +1,37 @@ +name: reflex-examples-FileReader +version: 0.1.0.0 +synopsis: Examples for reflex and reflex-dom +-- description: +homepage: https://github.com/reflex-frp/reflex-examples +license: BSD3 +license-file: LICENSE +author: Divam +maintainer: dfordivam@gmail.com +-- copyright: +category: Web +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +executable reflex-examples-FileReader + main-is: Main.hs + other-modules: Frontend.Examples.FileReader.Main + -- other-extensions: + build-depends: base >=4.11 && <4.12 + , bytestring + , containers + , ghcjs-dom + , jsaddle + , lens + , modern-uri + , reflex + , reflex-dom + , random + , time + , vector + , unordered-containers + , text + , aeson + , array + -- , jsaddle-warp + default-language: Haskell2010 diff --git a/reflex-dom-projects/generated/FileReader/Frontend/Examples/FileReader/Main.hs b/reflex-dom-projects/generated/FileReader/Frontend/Examples/FileReader/Main.hs new file mode 100644 index 0000000..5796deb --- /dev/null +++ b/reflex-dom-projects/generated/FileReader/Frontend/Examples/FileReader/Main.hs @@ -0,0 +1,68 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE PartialTypeSignatures #-} +{-# LANGUAGE TypeFamilies #-} + +module Frontend.Examples.FileReader.Main where + +import Control.Monad ((<=<), void) +import Data.Maybe (listToMaybe) +import Data.Monoid ((<>)) +import Data.Text (Text) +import qualified Data.Text as T +import GHCJS.DOM.EventM (on) +import GHCJS.DOM.FileReader (newFileReader, readAsDataURL, load + , getResult) +import Language.Javascript.JSaddle +import Reflex.Dom + +app + :: ( DomBuilder t m + , MonadHold t m + , PerformEvent t m + , TriggerEvent t m + , Prerender js m + ) + => m () +app = do + header + filesDyn <- fileInputElement + urlE <- fmap (ffilter ("data:image" `T.isPrefixOf`)) + . dataURLFileReader + . fmapMaybe listToMaybe + . updated $ filesDyn + el "br" blank + void $ el "div" + . widgetHold blank + . ffor urlE $ \url -> + elAttr "img" ("src" =: url <> "style" =: "max-width: 80%") blank + +fileInputElement :: DomBuilder t m => m (Dynamic t [RawFile (DomBuilderSpace m)]) +fileInputElement = do + ie <- inputElement $ def + & inputElementConfig_elementConfig . elementConfig_initialAttributes .~ + ("type" =: "file" <> "accept" =: "image/png, image/jpeg") + return (_inputElement_files ie) + +dataURLFileReader + :: ( DomBuilder t m + , TriggerEvent t m + , PerformEvent t m + , Prerender js m + ) + => Event t (RawFile (DomBuilderSpace m)) -> m (Event t Text) +dataURLFileReader request = prerender (return never) $ do + fileReader <- liftJSM newFileReader + performEvent_ (fmap (readAsDataURL fileReader . Just) request) + e <- wrapDomEvent fileReader (`on` load) . liftJSM $ do + v <- getResult fileReader + (fromJSVal <=< toJSVal) v + return (fmapMaybe id e) + +header :: DomBuilder t m => m () +header = do + el "strong" $ do + text " FileReader test page" + el "p" $ + text "Select an image file. It will be shown below" diff --git a/reflex-dom-projects/generated/FileReader/Main.hs b/reflex-dom-projects/generated/FileReader/Main.hs new file mode 100644 index 0000000..60c0ce4 --- /dev/null +++ b/reflex-dom-projects/generated/FileReader/Main.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Frontend.Examples.FileReader.Main +import Reflex.Dom +import Data.Map (Map) +import Data.Text (Text) +import qualified Data.Text as T + +main :: IO () +main = mainWidgetWithHead pageHead $ do + article $ app + +-- | An @
@ tag that will set its title and the class of its child +-- @
@ based on the current route +article + :: ( DomBuilder t m + , PostBuild t m + ) + => m () -- ^ Article content widget + -> m () +article c = el "main" $ el "article" c + +pageHead :: DomBuilder t m => m () +pageHead = do + el "title" $ text "Reflex FRP Examples" + elAttr "meta" metaDesc blank + elAttr "meta" metaKeywords blank + elAttr "meta" viewport blank + styleSheet $ "static/css/normalize.css" + styleSheet $ "static/css/fontawesome.min.css" + styleSheet $ "static/css/font.css" + styleSheet $ "static/css/style.css" + -- elAttr "script" ("type" =: "text/javascript" <> "src" =: static @"echarts.min.js") blank + +metaDesc :: Map Text Text +metaDesc = "name" =: "description" + <> "content" =: "Reflex Functional Reactive Programming Examples" + +metaKeywords :: Map Text Text +metaKeywords = "name" =: "keywords" + <> "content" =: "reflex, reflex frp, functional reactive programming, haskell, framework, reflex dom" + +viewport :: Map Text Text +viewport = "name" =: "viewport" + <> "content" =: "width=device-width, initial-scale=1" + +-- styleSheet are functions to add links to html +styleSheet :: DomBuilder t m => Text -> m () +styleSheet myLink = elAttr "link" attrs blank + where attrs = "rel" =: "stylesheet" + <> "type" =: "text/css" + <> "href" =: myLink + +tshow :: Show a => a -> Text +tshow = T.pack . show diff --git a/reflex-dom-projects/generated/FileReader/Setup.hs b/reflex-dom-projects/generated/FileReader/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/reflex-dom-projects/generated/FileReader/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/reflex-dom-projects/generated/NasaPod/Frontend/Examples/NasaPod/Main.hs b/reflex-dom-projects/generated/NasaPod/Frontend/Examples/NasaPod/Main.hs new file mode 100644 index 0000000..f9b4e04 --- /dev/null +++ b/reflex-dom-projects/generated/NasaPod/Frontend/Examples/NasaPod/Main.hs @@ -0,0 +1,90 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecursiveDo #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Frontend.Examples.NasaPod.Main where + +import Data.Aeson (FromJSON, ToJSON) +import Data.Map (Map) +import Data.Monoid ((<>)) +import qualified Data.Text as T +import Data.Text (Text) +import GHC.Generics (Generic) +import Reflex.Dom +import Control.Monad.Fix (MonadFix) + +data Apod = + Apod { date :: T.Text + , explanation :: T.Text + , hdurl :: T.Text + , media_type :: T.Text + , service_version :: T.Text + , title :: T.Text + , url :: T.Text + } + deriving (Generic, Show) + +instance FromJSON Apod +instance ToJSON Apod + +app + :: ( DomBuilder t m + , MonadFix m + , MonadHold t m + , PostBuild t m + , PerformEvent t m + , TriggerEvent t m + , Prerender js m + ) + => m () +app = el "div" $ do + apiKey <- inputElement $ def & initialAttributes .~ ("placeholder" =: "Enter NASA API Key") + submitEvent <- button "Submit" + let submitApiKey = tagPromptlyDyn (value apiKey) submitEvent + loadingWidget = el "div" $ do + el "p" $ do + text "Get your API key from the " + elAttr "a" ("href" =: "https://api.nasa.gov/" <> "target" =: "_blank") $ text "NASA Open API" + text " site" + _ <- widgetHold loadingWidget $ fmap apod submitApiKey + return () + +apod + :: ( DomBuilder t m + , MonadFix m + , PerformEvent t m + , MonadHold t m + , PostBuild t m + , TriggerEvent t m + , Prerender js m + ) + => Text + -> m () +apod apiKey = prerender (blank) $ do + pb :: Event t () <- getPostBuild + let + defReq = "https://api.nasa.gov/planetary/apod?api_key=" <> apiKey + xhrTxt :: Maybe T.Text -> T.Text + xhrTxt = maybe defReq (\d -> defReq <> "&date=" <> d) + req md = XhrRequest "GET" (xhrTxt md) def + rec + eRsp :: Event t XhrResponse <- performRequestAsync $ req <$> + leftmost [ Nothing <$ pb + , fmap Just eValidDate + ] + let + eRspApod :: Event t Apod = fmapMaybe decodeXhrResponse eRsp + imgUrl :: Event t T.Text = fmap url eRspApod + explEv :: Event t T.Text = fmap explanation eRspApod + srcAttr :: Event t (Map T.Text T.Text) = ffor imgUrl $ \u -> "src" =: u + srcAttrDyn :: Dynamic t (Map T.Text T.Text) + <- holdDyn mempty srcAttr + elDynAttr "img" srcAttrDyn $ return () + date <- textInput $ def & + attributes .~ constDyn ("placeholder" =: "YYYY-MM-DD") + let eValidDate = ffilter ((==10) . T.length) $ updated $ value date + el "p" $ + dynText =<< holdDyn "Waiting for response" explEv + return () + diff --git a/reflex-dom-projects/generated/NasaPod/Main.hs b/reflex-dom-projects/generated/NasaPod/Main.hs new file mode 100644 index 0000000..5247b9f --- /dev/null +++ b/reflex-dom-projects/generated/NasaPod/Main.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Frontend.Examples.NasaPod.Main +import Reflex.Dom +import Data.Map (Map) +import Data.Text (Text) +import qualified Data.Text as T + +main :: IO () +main = mainWidgetWithHead pageHead $ do + article $ app + +-- | An @
@ tag that will set its title and the class of its child +-- @
@ based on the current route +article + :: ( DomBuilder t m + , PostBuild t m + ) + => m () -- ^ Article content widget + -> m () +article c = el "main" $ el "article" c + +pageHead :: DomBuilder t m => m () +pageHead = do + el "title" $ text "Reflex FRP Examples" + elAttr "meta" metaDesc blank + elAttr "meta" metaKeywords blank + elAttr "meta" viewport blank + styleSheet $ "static/css/normalize.css" + styleSheet $ "static/css/fontawesome.min.css" + styleSheet $ "static/css/font.css" + styleSheet $ "static/css/style.css" + -- elAttr "script" ("type" =: "text/javascript" <> "src" =: static @"echarts.min.js") blank + +metaDesc :: Map Text Text +metaDesc = "name" =: "description" + <> "content" =: "Reflex Functional Reactive Programming Examples" + +metaKeywords :: Map Text Text +metaKeywords = "name" =: "keywords" + <> "content" =: "reflex, reflex frp, functional reactive programming, haskell, framework, reflex dom" + +viewport :: Map Text Text +viewport = "name" =: "viewport" + <> "content" =: "width=device-width, initial-scale=1" + +-- styleSheet are functions to add links to html +styleSheet :: DomBuilder t m => Text -> m () +styleSheet myLink = elAttr "link" attrs blank + where attrs = "rel" =: "stylesheet" + <> "type" =: "text/css" + <> "href" =: myLink + +tshow :: Show a => a -> Text +tshow = T.pack . show diff --git a/reflex-dom-projects/generated/NasaPod/NasaPod.cabal b/reflex-dom-projects/generated/NasaPod/NasaPod.cabal new file mode 100644 index 0000000..c850f16 --- /dev/null +++ b/reflex-dom-projects/generated/NasaPod/NasaPod.cabal @@ -0,0 +1,37 @@ +name: reflex-examples-NasaPod +version: 0.1.0.0 +synopsis: Examples for reflex and reflex-dom +-- description: +homepage: https://github.com/reflex-frp/reflex-examples +license: BSD3 +license-file: LICENSE +author: Divam +maintainer: dfordivam@gmail.com +-- copyright: +category: Web +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +executable reflex-examples-NasaPod + main-is: Main.hs + other-modules: Frontend.Examples.NasaPod.Main + -- other-extensions: + build-depends: base >=4.11 && <4.12 + , bytestring + , containers + , ghcjs-dom + , jsaddle + , lens + , modern-uri + , reflex + , reflex-dom + , random + , time + , vector + , unordered-containers + , text + , aeson + , array + -- , jsaddle-warp + default-language: Haskell2010 diff --git a/reflex-dom-projects/generated/NasaPod/Setup.hs b/reflex-dom-projects/generated/NasaPod/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/reflex-dom-projects/generated/NasaPod/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/reflex-dom-projects/generated/ScreenKeyboard/Frontend/Examples/ScreenKeyboard/Main.hs b/reflex-dom-projects/generated/ScreenKeyboard/Frontend/Examples/ScreenKeyboard/Main.hs new file mode 100644 index 0000000..4961ee9 --- /dev/null +++ b/reflex-dom-projects/generated/ScreenKeyboard/Frontend/Examples/ScreenKeyboard/Main.hs @@ -0,0 +1,87 @@ +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecursiveDo #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE PartialTypeSignatures #-} +{-# LANGUAGE FlexibleContexts #-} + +module Frontend.Examples.ScreenKeyboard.Main where + +{- + - buttons + real keyboard both writing to a text box + -} + +import Control.Monad (forM, void) +import Control.Monad.Fix (MonadFix) +import qualified Data.List.NonEmpty as DL (head) +import Data.Monoid ((<>)) +import qualified Data.Text as T +import Data.Text (Text) +import GHCJS.DOM.HTMLElement (focus) +import GHCJS.DOM.HTMLInputElement hiding (setValue) +import Language.Javascript.JSaddle +import Reflex.Dom + +-- import Language.Javascript.JSaddle.Warp + + +insertAt :: Int -> Char -> T.Text -> T.Text +insertAt n c v = T.take n v <> T.singleton c <> T.drop n v + +fromListE :: Reflex t => [Event t a] -> Event t a +fromListE = fmap DL.head . mergeList + +performArg :: (PerformEvent t m, MonadJSM (Performable m)) + => (b -> JSM a) -> Event t b -> m (Event t a) +performArg f x = performEvent (fmap (liftJSM . f) x) + +inputW + :: ( DomBuilder t m + , Prerender js m + , PerformEvent t m + , MonadFix m + ) + => Event t Char + -> m () +inputW buttonE = do + rec + let + html = _inputElement_raw input -- html element + cur = current $ value input -- actual string + input <- inputElement $ def + & inputElementConfig_setValue .~ fmap snd newStringE + newStringE <- doStuff cur html buttonE + return () + +doStuff + :: ( DomBuilder t m + , Prerender js m + , PerformEvent t m + ) + => Behavior t Text + -> RawInputElement (DomBuilderSpace m) + -> Event t Char + -> m (Event t (Int, Text)) +doStuff cur html buttonE = do + posCharE :: Event t (Char, Int) <- prerender (return never) $ do + ev <- performArg (\c -> (,) c <$> getSelectionStart html) buttonE + void $ (flip performArg) (fmap snd ev) $ \n -> do + setSelectionStart html (n + 1) + setSelectionEnd html (n + 1) + void $ performArg (const $ focus html) buttonE -- keep the focus right + return ev + let + newStringE = attachWith (\v (c, n) -> (n + 1, insertAt n c v)) cur posCharE + return newStringE + +keys :: DomBuilder t m => m [Event t Char] +keys = forM "qwerty" $ \c -> fmap (const c) <$> button [c] -- OverloadedLists + +app + :: ( DomBuilder t m + , MonadFix m + , PerformEvent t m + , Prerender js m + ) + => m () +app = el "div" $ elClass "div" "keys" keys >>= inputW . fromListE diff --git a/reflex-dom-projects/generated/ScreenKeyboard/Main.hs b/reflex-dom-projects/generated/ScreenKeyboard/Main.hs new file mode 100644 index 0000000..6a443de --- /dev/null +++ b/reflex-dom-projects/generated/ScreenKeyboard/Main.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Frontend.Examples.ScreenKeyboard.Main +import Reflex.Dom +import Data.Map (Map) +import Data.Text (Text) +import qualified Data.Text as T + +main :: IO () +main = mainWidgetWithHead pageHead $ do + article $ app + +-- | An @
@ tag that will set its title and the class of its child +-- @
@ based on the current route +article + :: ( DomBuilder t m + , PostBuild t m + ) + => m () -- ^ Article content widget + -> m () +article c = el "main" $ el "article" c + +pageHead :: DomBuilder t m => m () +pageHead = do + el "title" $ text "Reflex FRP Examples" + elAttr "meta" metaDesc blank + elAttr "meta" metaKeywords blank + elAttr "meta" viewport blank + styleSheet $ "static/css/normalize.css" + styleSheet $ "static/css/fontawesome.min.css" + styleSheet $ "static/css/font.css" + styleSheet $ "static/css/style.css" + -- elAttr "script" ("type" =: "text/javascript" <> "src" =: static @"echarts.min.js") blank + +metaDesc :: Map Text Text +metaDesc = "name" =: "description" + <> "content" =: "Reflex Functional Reactive Programming Examples" + +metaKeywords :: Map Text Text +metaKeywords = "name" =: "keywords" + <> "content" =: "reflex, reflex frp, functional reactive programming, haskell, framework, reflex dom" + +viewport :: Map Text Text +viewport = "name" =: "viewport" + <> "content" =: "width=device-width, initial-scale=1" + +-- styleSheet are functions to add links to html +styleSheet :: DomBuilder t m => Text -> m () +styleSheet myLink = elAttr "link" attrs blank + where attrs = "rel" =: "stylesheet" + <> "type" =: "text/css" + <> "href" =: myLink + +tshow :: Show a => a -> Text +tshow = T.pack . show diff --git a/reflex-dom-projects/generated/ScreenKeyboard/ScreenKeyboard.cabal b/reflex-dom-projects/generated/ScreenKeyboard/ScreenKeyboard.cabal new file mode 100644 index 0000000..065ceaa --- /dev/null +++ b/reflex-dom-projects/generated/ScreenKeyboard/ScreenKeyboard.cabal @@ -0,0 +1,37 @@ +name: reflex-examples-ScreenKeyboard +version: 0.1.0.0 +synopsis: Examples for reflex and reflex-dom +-- description: +homepage: https://github.com/reflex-frp/reflex-examples +license: BSD3 +license-file: LICENSE +author: Divam +maintainer: dfordivam@gmail.com +-- copyright: +category: Web +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +executable reflex-examples-ScreenKeyboard + main-is: Main.hs + other-modules: Frontend.Examples.ScreenKeyboard.Main + -- other-extensions: + build-depends: base >=4.11 && <4.12 + , bytestring + , containers + , ghcjs-dom + , jsaddle + , lens + , modern-uri + , reflex + , reflex-dom + , random + , time + , vector + , unordered-containers + , text + , aeson + , array + -- , jsaddle-warp + default-language: Haskell2010 diff --git a/reflex-dom-projects/generated/ScreenKeyboard/Setup.hs b/reflex-dom-projects/generated/ScreenKeyboard/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/reflex-dom-projects/generated/ScreenKeyboard/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/reflex-dom-projects/generated/TicTacToe/Frontend/Examples/TicTacToe/Main.hs b/reflex-dom-projects/generated/TicTacToe/Frontend/Examples/TicTacToe/Main.hs new file mode 100644 index 0000000..f5c0c82 --- /dev/null +++ b/reflex-dom-projects/generated/TicTacToe/Frontend/Examples/TicTacToe/Main.hs @@ -0,0 +1,171 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecursiveDo #-} +{-# LANGUAGE LambdaCase #-} + +module Frontend.Examples.TicTacToe.Main + (app) + where + +import qualified Data.Text as T +import Data.Text (Text) +import Control.Monad (forM) +import Control.Monad.Fix (MonadFix) +import Data.List (elem) +import Reflex.Dom +import Data.Array (Array, Ix) +import qualified Data.Array as A + +data Row = + Row_1 + | Row_2 + | Row_3 + deriving (Eq, Show, Ord, Enum, Bounded, Ix) + +data Col = + Col_1 + | Col_2 + | Col_3 + deriving (Eq, Show, Ord, Enum, Bounded, Ix) + +data Player = + Player_X + | Player_O + deriving (Eq, Show, Ord, Enum, Bounded) + +winningPos :: [[(Row, Col)]] +winningPos = + [[(a, b) | a <- [Row_1 .. ]] | b <- [Col_1 .. ]] + <> [[(a, b) | b <- [Col_1 .. ]] | a <- [Row_1 .. ]] + <> [ [(Row_1, Col_1), (Row_2, Col_2), (Row_3, Col_3)] + , [(Row_1, Col_3), (Row_2, Col_2), (Row_3, Col_1)] + ] + +-- This is not really required, as the Moves is sufficient +type Squares = Array (Row, Col) (Maybe Player) +type MovePos = (Row, Col) +type Moves = [MovePos] + +data State = State + { state_currentMove :: Int + , state_moves :: Moves + } + +data Action = + Action_GoToMove Int + | Action_DoMove MovePos + +app + :: ( DomBuilder t m + , MonadFix m + , MonadHold t m + , PostBuild t m + ) + => m () +app = divClass "game" $ do + let initState = State 0 [] + + rec + let + squares = ffor stateDyn $ \(State p mvs) -> makeSquares $ take p mvs + stateDyn <- foldDynMaybe changeGameState initState $ + leftmost [goToMove, newMove] + newMove <- gameBoard squares + goToMove <- gameInfo stateDyn + return () + +makeSquares :: Moves -> Squares +makeSquares mvs = A.accumArray (flip const) Nothing ((Row_1, Col_1), (Row_3, Col_3)) ls + where + ls = zip mvs $ concat $ repeat [Just Player_X, Just Player_O] + +changeGameState :: Action -> State -> Maybe State +changeGameState a (State p moves) = case a of + (Action_GoToMove i) -> Just $ State i moves + (Action_DoMove m) -> case (checkWon curMoves, elem m curMoves) of + (Nothing, False) -> Just $ State (p + 1) newMoves + _ -> Nothing + where + newMoves = curMoves ++ [m] + curMoves = take p moves + +checkWon :: Moves -> Maybe Player +checkWon mvs = case (isWin pX, isWin pO) of + (True, _) -> Just Player_X + (False, True) -> Just Player_O + (False, False) -> Nothing + where + -- find if any set of winning moves are present in player' moves + isWin pmvs = any (\wmvs -> all (\wm -> elem wm pmvs) wmvs) winningPos + -- divide moves for X and O + (pX, pO) = f mvs Player_X ([], []) + f [] _ v = v + f (m:ms) Player_X (xs, os)= f ms Player_O (xs ++ [m], os) + f (m:ms) Player_O (xs, os)= f ms Player_X (xs, os ++ [m]) + +gameBoard + :: ( DomBuilder t m + , PostBuild t m + ) + => Dynamic t Squares + -> m (Event t Action) +gameBoard squares = divClass "game-board" $ do + evs <- forM [minBound .. maxBound] $ \row -> divClass "board-row" $ do + evs <- forM [minBound .. maxBound] $ \col -> do + let + square = (row, col) + style = "height: 2em; width: 2em; position: block;" + (btn1,_) <- elAttr' "button" ("style" =: style) $ + dynText (getSquareText square <$> squares) + return (square <$ domEvent Click btn1) + return (leftmost evs) + return $ Action_DoMove <$> leftmost evs + +-- | What to display on square +getSquareText :: (Row, Col) -> Squares -> Text +getSquareText square squares = case squares A.! square of + Nothing -> "." + (Just Player_X) -> "X" + (Just Player_O) -> "O" + +gameInfo + :: ( DomBuilder t m + , MonadFix m + , PostBuild t m + , MonadHold t m + ) + => Dynamic t State + -> m (Event t Action) +gameInfo stateDyn = divClass "game-info" $ do + let + nextPlayer = ffor stateDyn $ \(State p _) -> if even p + then Player_X + else Player_O + moveCount = (length . state_moves <$> stateDyn) + + el "div" $ do + dyn_ $ ffor (checkWon . state_moves <$> stateDyn) $ \case + Nothing -> do + text "Next player: " + dynText $ ffor nextPlayer $ \case + Player_X -> "X" + Player_O -> "O" + (Just p) -> do + text "Winner player: " + text $ case p of + Player_X -> "X" + Player_O -> "O" + + el "ol" $ do + ev1 <- el "li" $ do + button "Go to game start" + + let + moves = ffor moveCount $ \i -> [1..i] + evDyn <- simpleList moves $ \iDyn -> do + i <- sample $ current iDyn + el "li" $ do + ev <- button $ "Go to move #" <> T.pack (show i) + return (Action_GoToMove i <$ ev) + + let ev2 = switch (current $ leftmost <$> evDyn) + return $ leftmost $ [Action_GoToMove 0 <$ ev1, ev2] diff --git a/reflex-dom-projects/generated/TicTacToe/Main.hs b/reflex-dom-projects/generated/TicTacToe/Main.hs new file mode 100644 index 0000000..7ed8795 --- /dev/null +++ b/reflex-dom-projects/generated/TicTacToe/Main.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Frontend.Examples.TicTacToe.Main +import Reflex.Dom +import Data.Map (Map) +import Data.Text (Text) +import qualified Data.Text as T + +main :: IO () +main = mainWidgetWithHead pageHead $ do + article $ app + +-- | An @
@ tag that will set its title and the class of its child +-- @
@ based on the current route +article + :: ( DomBuilder t m + , PostBuild t m + ) + => m () -- ^ Article content widget + -> m () +article c = el "main" $ el "article" c + +pageHead :: DomBuilder t m => m () +pageHead = do + el "title" $ text "Reflex FRP Examples" + elAttr "meta" metaDesc blank + elAttr "meta" metaKeywords blank + elAttr "meta" viewport blank + styleSheet $ "static/css/normalize.css" + styleSheet $ "static/css/fontawesome.min.css" + styleSheet $ "static/css/font.css" + styleSheet $ "static/css/style.css" + -- elAttr "script" ("type" =: "text/javascript" <> "src" =: static @"echarts.min.js") blank + +metaDesc :: Map Text Text +metaDesc = "name" =: "description" + <> "content" =: "Reflex Functional Reactive Programming Examples" + +metaKeywords :: Map Text Text +metaKeywords = "name" =: "keywords" + <> "content" =: "reflex, reflex frp, functional reactive programming, haskell, framework, reflex dom" + +viewport :: Map Text Text +viewport = "name" =: "viewport" + <> "content" =: "width=device-width, initial-scale=1" + +-- styleSheet are functions to add links to html +styleSheet :: DomBuilder t m => Text -> m () +styleSheet myLink = elAttr "link" attrs blank + where attrs = "rel" =: "stylesheet" + <> "type" =: "text/css" + <> "href" =: myLink + +tshow :: Show a => a -> Text +tshow = T.pack . show diff --git a/reflex-dom-projects/generated/TicTacToe/Setup.hs b/reflex-dom-projects/generated/TicTacToe/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/reflex-dom-projects/generated/TicTacToe/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/reflex-dom-projects/generated/TicTacToe/TicTacToe.cabal b/reflex-dom-projects/generated/TicTacToe/TicTacToe.cabal new file mode 100644 index 0000000..f8b3457 --- /dev/null +++ b/reflex-dom-projects/generated/TicTacToe/TicTacToe.cabal @@ -0,0 +1,37 @@ +name: reflex-examples-TicTacToe +version: 0.1.0.0 +synopsis: Examples for reflex and reflex-dom +-- description: +homepage: https://github.com/reflex-frp/reflex-examples +license: BSD3 +license-file: LICENSE +author: Divam +maintainer: dfordivam@gmail.com +-- copyright: +category: Web +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +executable reflex-examples-TicTacToe + main-is: Main.hs + other-modules: Frontend.Examples.TicTacToe.Main + -- other-extensions: + build-depends: base >=4.11 && <4.12 + , bytestring + , containers + , ghcjs-dom + , jsaddle + , lens + , modern-uri + , reflex + , reflex-dom + , random + , time + , vector + , unordered-containers + , text + , aeson + , array + -- , jsaddle-warp + default-language: Haskell2010 diff --git a/reflex-dom-projects/generated/WebSocketEcho/Frontend/Examples/WebSocketEcho/Main.hs b/reflex-dom-projects/generated/WebSocketEcho/Frontend/Examples/WebSocketEcho/Main.hs new file mode 100644 index 0000000..6a27790 --- /dev/null +++ b/reflex-dom-projects/generated/WebSocketEcho/Frontend/Examples/WebSocketEcho/Main.hs @@ -0,0 +1,46 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecursiveDo #-} + +module Frontend.Examples.WebSocketEcho.Main where + +import Data.Text.Encoding (encodeUtf8, decodeUtf8) +import Reflex.Dom +import Control.Monad.Fix (MonadFix) + +app + :: ( DomBuilder t m + , MonadFix m + , MonadHold t m + , PostBuild t m + , PerformEvent t m + , TriggerEvent t m + , Prerender js m + ) + => m () +app = do + header + rec t <- inputElement $ def & inputElementConfig_setValue .~ fmap (const "") newMessage + b <- button "Send" + let newMessage = fmap ((:[]) . encodeUtf8) + $ tag (current $ value t) + $ leftmost [b, keypress Enter t] + + receivedMessages <- prerender (return (constDyn [])) $ do + ws <- webSocket "wss://echo.websocket.org" $ def + & webSocketConfig_send .~ newMessage + foldDyn (\m ms -> ms ++ [m]) [] $ _webSocket_recv ws + + el "p" $ text "Responses from the WebSocket.org echo service:" + _ <- el "ul" + $ simpleList receivedMessages + $ \m -> el "li" $ dynText $ fmap decodeUtf8 m + return () + +header :: DomBuilder t m => m () +header = do + el "strong" $ do + text " WebSocket test page" + el "p" $ do + text "Send a message to the WebSocket.org: https://www.websocket.org/echo.html" + text "'s websocket echo service:" diff --git a/reflex-dom-projects/generated/WebSocketEcho/Main.hs b/reflex-dom-projects/generated/WebSocketEcho/Main.hs new file mode 100644 index 0000000..70606bc --- /dev/null +++ b/reflex-dom-projects/generated/WebSocketEcho/Main.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Frontend.Examples.WebSocketEcho.Main +import Reflex.Dom +import Data.Map (Map) +import Data.Text (Text) +import qualified Data.Text as T + +main :: IO () +main = mainWidgetWithHead pageHead $ do + article $ app + +-- | An @
@ tag that will set its title and the class of its child +-- @
@ based on the current route +article + :: ( DomBuilder t m + , PostBuild t m + ) + => m () -- ^ Article content widget + -> m () +article c = el "main" $ el "article" c + +pageHead :: DomBuilder t m => m () +pageHead = do + el "title" $ text "Reflex FRP Examples" + elAttr "meta" metaDesc blank + elAttr "meta" metaKeywords blank + elAttr "meta" viewport blank + styleSheet $ "static/css/normalize.css" + styleSheet $ "static/css/fontawesome.min.css" + styleSheet $ "static/css/font.css" + styleSheet $ "static/css/style.css" + -- elAttr "script" ("type" =: "text/javascript" <> "src" =: static @"echarts.min.js") blank + +metaDesc :: Map Text Text +metaDesc = "name" =: "description" + <> "content" =: "Reflex Functional Reactive Programming Examples" + +metaKeywords :: Map Text Text +metaKeywords = "name" =: "keywords" + <> "content" =: "reflex, reflex frp, functional reactive programming, haskell, framework, reflex dom" + +viewport :: Map Text Text +viewport = "name" =: "viewport" + <> "content" =: "width=device-width, initial-scale=1" + +-- styleSheet are functions to add links to html +styleSheet :: DomBuilder t m => Text -> m () +styleSheet myLink = elAttr "link" attrs blank + where attrs = "rel" =: "stylesheet" + <> "type" =: "text/css" + <> "href" =: myLink + +tshow :: Show a => a -> Text +tshow = T.pack . show diff --git a/reflex-dom-projects/generated/WebSocketEcho/Setup.hs b/reflex-dom-projects/generated/WebSocketEcho/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/reflex-dom-projects/generated/WebSocketEcho/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/reflex-dom-projects/generated/WebSocketEcho/WebSocketEcho.cabal b/reflex-dom-projects/generated/WebSocketEcho/WebSocketEcho.cabal new file mode 100644 index 0000000..19720b4 --- /dev/null +++ b/reflex-dom-projects/generated/WebSocketEcho/WebSocketEcho.cabal @@ -0,0 +1,37 @@ +name: reflex-examples-WebSocketEcho +version: 0.1.0.0 +synopsis: Examples for reflex and reflex-dom +-- description: +homepage: https://github.com/reflex-frp/reflex-examples +license: BSD3 +license-file: LICENSE +author: Divam +maintainer: dfordivam@gmail.com +-- copyright: +category: Web +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +executable reflex-examples-WebSocketEcho + main-is: Main.hs + other-modules: Frontend.Examples.WebSocketEcho.Main + -- other-extensions: + build-depends: base >=4.11 && <4.12 + , bytestring + , containers + , ghcjs-dom + , jsaddle + , lens + , modern-uri + , reflex + , reflex-dom + , random + , time + , vector + , unordered-containers + , text + , aeson + , array + -- , jsaddle-warp + default-language: Haskell2010 diff --git a/reflex-dom-projects/template/Main.hs b/reflex-dom-projects/template/Main.hs new file mode 100644 index 0000000..6753d2c --- /dev/null +++ b/reflex-dom-projects/template/Main.hs @@ -0,0 +1,56 @@ +{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Frontend.Examples.EXAMPLENAME.Main +import Reflex.Dom +import Data.Map (Map) +import Data.Text (Text) +import qualified Data.Text as T + +main :: IO () +main = mainWidgetWithHead pageHead $ do + article $ app + +-- | An @
@ tag that will set its title and the class of its child +-- @
@ based on the current route +article + :: ( DomBuilder t m + , PostBuild t m + ) + => m () -- ^ Article content widget + -> m () +article c = el "main" $ el "article" c + +pageHead :: DomBuilder t m => m () +pageHead = do + el "title" $ text "Reflex FRP Examples" + elAttr "meta" metaDesc blank + elAttr "meta" metaKeywords blank + elAttr "meta" viewport blank + styleSheet $ "static/css/normalize.css" + styleSheet $ "static/css/fontawesome.min.css" + styleSheet $ "static/css/font.css" + styleSheet $ "static/css/style.css" + -- elAttr "script" ("type" =: "text/javascript" <> "src" =: static @"echarts.min.js") blank + +metaDesc :: Map Text Text +metaDesc = "name" =: "description" + <> "content" =: "Reflex Functional Reactive Programming Examples" + +metaKeywords :: Map Text Text +metaKeywords = "name" =: "keywords" + <> "content" =: "reflex, reflex frp, functional reactive programming, haskell, framework, reflex dom" + +viewport :: Map Text Text +viewport = "name" =: "viewport" + <> "content" =: "width=device-width, initial-scale=1" + +-- styleSheet are functions to add links to html +styleSheet :: DomBuilder t m => Text -> m () +styleSheet myLink = elAttr "link" attrs blank + where attrs = "rel" =: "stylesheet" + <> "type" =: "text/css" + <> "href" =: myLink + +tshow :: Show a => a -> Text +tshow = T.pack . show diff --git a/reflex-dom-projects/template/Setup.hs b/reflex-dom-projects/template/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/reflex-dom-projects/template/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/reflex-dom-projects/template/template.cabal b/reflex-dom-projects/template/template.cabal new file mode 100644 index 0000000..e392647 --- /dev/null +++ b/reflex-dom-projects/template/template.cabal @@ -0,0 +1,37 @@ +name: reflex-examples-EXAMPLENAME +version: 0.1.0.0 +synopsis: Examples for reflex and reflex-dom +-- description: +homepage: https://github.com/reflex-frp/reflex-examples +license: BSD3 +license-file: LICENSE +author: Divam +maintainer: dfordivam@gmail.com +-- copyright: +category: Web +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +executable reflex-examples-EXAMPLENAME + main-is: Main.hs + other-modules: Frontend.Examples.EXAMPLENAME.Main + -- other-extensions: + build-depends: base >=4.11 && <4.12 + , bytestring + , containers + , ghcjs-dom + , jsaddle + , lens + , modern-uri + , reflex + , reflex-dom + , random + , time + , vector + , unordered-containers + , text + , aeson + , array + -- , jsaddle-warp + default-language: Haskell2010