packages feed

miso 1.2.0.0 → 1.3.0.0

raw patch · 9 files changed

+187/−23 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Miso.Html: onBeforeDestroyed :: action -> Attribute action

Files

README.md view
@@ -9,12 +9,12 @@  <p align="center">   <a href="https://haskell-miso-slack.herokuapp.com">-	<img src="https://img.shields.io/badge/slack-miso-E01563.svg?style=flat-square" alt="Miso Slack">+<img src="https://img.shields.io/badge/slack-miso-E01563.svg?style=flat-square" alt="Miso Slack">   </a>   <a href="https://haskell.org"> 	<img src="https://img.shields.io/badge/language-Haskell-orange.svg?style=flat-square" alt="Haskell">   </a>-  <a href="https://haskell-miso.cachix.org">+  <a href="https://miso-haskell.cachix.org"> 	<img src="https://img.shields.io/badge/build-cachix-yellow.svg?style=flat-square" alt="Cachix">   </a>   <a href="https://travis-ci.org/dmjio/miso">@@ -59,6 +59,8 @@   - [GHC](#ghc)   - [GHCJS](#ghcjs) - [Sample Application](#sample-application)+- [Transition Application](#transition-application)+- [Live reload with JSaddle](#live-reload-with-jsaddle) - [Building examples](#building-examples) - [Coverage](#coverage) - [Isomorphic](#isomorphic)@@ -66,11 +68,13 @@ - [Binary cache](#binary-cache) - [Benchmarks](#benchmarks) - [Maintainers](#maintainers)+- [Commercial Users](#commercial-users) - [Contributing](#contributing)+- [Contributors](#contributors) - [License](#license)  ## Quick start-To get started quickly building applications, we recommend using the [`nix`](https://nixos.org/nix) package manager with miso's binary cache provided by [`cachix`](https://haskell-miso.cachix.org/). It is possible to use [`stack`](https://docs.haskellstack.org/en/stable/README/) to build GHCJS projects, but support for procuring `GHCJS` has been removed [as of stack 2.0](https://github.com/commercialhaskell/stack/issues/4086). `nix` is used to procure a working version of `GHCJS`. If you're using `cabal` we assume you have [obtained `GHCJS`](https://github.com/ghcjs/ghcjs#installation) by other means. All source code depicted below for the quick start app is available [here](https://github.com/dmjio/miso/tree/master/sample-app).+To get started quickly building applications, we recommend using the [`nix`](https://nixos.org/nix) package manager with miso's binary cache provided by [`cachix`](https://miso-haskell.cachix.org/). It is possible to use [`stack`](https://docs.haskellstack.org/en/stable/README/) to build GHCJS projects, but support for procuring `GHCJS` has been removed [as of stack 2.0](https://github.com/commercialhaskell/stack/issues/4086). `nix` is used to procure a working version of `GHCJS`. If you're using `cabal` we assume you have [obtained `GHCJS`](https://github.com/ghcjs/ghcjs#installation) by other means. All source code depicted below for the quick start app is available [here](https://github.com/dmjio/miso/tree/master/sample-app).  ### Begin To build the sample-app with `nix`, execute the commands below:@@ -79,7 +83,7 @@ # optional use of cache nix-env -iA cachix -f https://cachix.org/api/v1/install # optional use of cache-cachix use haskell-miso+cachix use miso-haskell git clone https://github.com/dmjio/miso cd miso/sample-app nix-build@@ -125,11 +129,10 @@  ```nix with (import (builtins.fetchTarball {-  url = "https://github.com/dmjio/miso/archive/39b9e26ff41d6aab3b9d13a9d102ac56017f6a1f.tar.gz";-  sha256 = "1lwr35p9074b7wgz0jh4f2pjc7ls8isgzmn9xl86vb6cvsm035kf";+  url = "https://github.com/dmjio/miso/archive/425de4eb87e876428df4872a595ac1a0717dd165.tar.gz";+  sha256 = "1mc44vnx8qmvxkrvxhzlnrza8shnk8a0ad7hfk8hlblzrd9ha5id"; }) {});-with pkgs.haskell.packages;-ghcjs.callCabal2nix "app" ./. {}+pkgs.haskell.packages.ghcjs.callCabal2nix "app" ./. {} ```  Add the source from [Sample Application](#sample-application) to `app/Main.hs`@@ -166,7 +169,7 @@  For incremental development inside of the `nix-shell` we recommend using a tool like [`entr`](http://eradman.com/entrproject/) to automatically rebuild on file changes, or roll your own solution with `inotify`. ```-ag -l | entr 'cabal build'+ag -l | entr cabal build ```  ### Architecture@@ -268,11 +271,16 @@  -- | Updates model, optionally introduces side effects updateModel :: Action -> Model -> Effect Action Model-updateModel AddOne m = noEff (m + 1)-updateModel SubtractOne m = noEff (m - 1)-updateModel NoOp m = noEff m-updateModel SayHelloWorld m = m <# do-  putStrLn "Hello World" >> pure NoOp+updateModel action m =+  case action of+    AddOne+      -> noEff (m + 1)+    SubtractOne+      -> noEff (m - 1)+    NoOp+      -> noEff m+    SayHelloWorld+      -> m <# do consoleLog "Hello World" >> pure NoOp  -- | Constructs a virtual DOM from a model viewModel :: Model -> View Action@@ -283,6 +291,83 @@  ] ``` +## Transition application++An alternative, more powerful interface for constructing `miso` applications is using the `Transition` interface.+`Transition` is based on the `StateT` monad transformer, and can be used to construct components. It also works+very nicely with lenses based on `MonadState` (i.e. `(.=)`, `(%=)`,`(+=)`,`(-=)`).+++```haskell+-- | Haskell language pragma+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}++-- | Haskell module declaration+module Main where++-- | Miso framework import+import Miso+import Miso.String++-- | Lens import+import Control.Lens++-- | Type synonym for an application model+data Model+  = Model+  { _counter :: Int+  } deriving (Show, Eq)++counter :: Lens' Model Int+counter = lens _counter $ \record field -> record { _counter = field }++-- | Sum type for application events+data Action+  = AddOne+  | SubtractOne+  | NoOp+  | SayHelloWorld+  deriving (Show, Eq)++-- | Entry point for a miso application+main :: IO ()+main = startApp App {..}+  where+    initialAction = SayHelloWorld -- initial action to be executed on application load+    model  = 0                    -- initial model+    update = fromTransition . updateModel -- update function+    view   = viewModel            -- view function+    events = defaultEvents        -- default delegated events+    subs   = []                   -- empty subscription list+    mountPoint = Nothing          -- mount point for application (Nothing defaults to 'body')++-- | Updates model, optionally introduces side effects+updateModel :: Action -> Transition Action Model ()+updateModel action =+  case action of+    AddOne+      -> counter += 1+    SubtractOne+      -> counter -= 1+    NoOp+      -> pure ()+    SayHelloWorld+      -> scheduleIO_ (consoleLog "Hello World")++-- | Constructs a virtual DOM from a model+viewModel :: Model -> View Action+viewModel x = div_ [] [+   button_ [ onClick AddOne ] [ text "+" ]+ , text (ms x)+ , button_ [ onClick SubtractOne ] [ text "-" ]+ ]+```++## Live reload with JSaddle++It is possible to build `miso` applications with `ghcid`, `jsaddle` that allow live reloading of your application in reponse to changes in application code. See the [README](https://github.com/dmjio/miso/blob/master/sample-app-jsaddle/README.md) in the `sample-app-jsaddle` folder for more information.+ ## Building examples  The easiest way to build the examples is with the [`nix`](https://nixos.org/nix/) package manager@@ -346,27 +431,61 @@  ## Binary cache -`nix` users on a Linux or OSX distro can take advantage of a [binary cache](https://haskell-miso.cachix.org) for faster builds. To use the binary cache follow the instructions on [cachix](https://haskell-miso.cachix.org/).+`nix` users on a Linux or OSX distro can take advantage of a [binary cache](https://miso-haskell.cachix.org) for faster builds. To use the binary cache follow the instructions on [cachix](https://miso-haskell.cachix.org/).  ```bash-cachix use haskell-miso+cachix use miso-haskell ```  ## Benchmarks  [According to benchmarks](https://rawgit.com/krausest/js-framework-benchmark/master/webdriver-ts-results/table.html), `miso` is among the fastest functional programming web frameworks, second only to [Elm](http://elm-lang.org). -<img src="https://cdn-images-1.medium.com/max/1600/1*6EjJTf1mhlTxd4QWsygCwA.png" width="500" height="600" />+<a target="_blank" href="https://rawgit.com/krausest/js-framework-benchmark/master/webdriver-ts-results/table.html"><img src="https://cdn-images-1.medium.com/max/1600/1*6EjJTf1mhlTxd4QWsygCwA.png" width="500" height="600" /></a>  ## Maintainers  [@dmjio](https://github.com/dmjio) +## Commercial Users+  - [Polimorphic](https://www.polimorphic.com)+  - [LumiGuide](https://lumi.guide/en/)+ ## Contributing  Feel free to dive in! [Open an issue](https://github.com/dmjio/miso/issues/new) or submit [PRs](https://github.com/dmjio/miso/pulls).  See [CONTRIBUTING](https://github.com/dmjio/miso/blob/master/CONTRIBUTING.md) for more info.++## Contributors++### Code Contributors++This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].+<a href="https://github.com/dmjio/miso/graphs/contributors"><img src="https://opencollective.com/miso/contributors.svg?width=890&button=false" /></a>++### Financial Contributors++Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/miso/contribute)]++#### Individuals++<a href="https://opencollective.com/miso"><img src="https://opencollective.com/miso/individuals.svg?width=890"></a>++#### Organizations++Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/miso/contribute)]++<a href="https://opencollective.com/miso/organization/0/website"><img src="https://opencollective.com/miso/organization/0/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/1/website"><img src="https://opencollective.com/miso/organization/1/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/2/website"><img src="https://opencollective.com/miso/organization/2/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/3/website"><img src="https://opencollective.com/miso/organization/3/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/4/website"><img src="https://opencollective.com/miso/organization/4/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/5/website"><img src="https://opencollective.com/miso/organization/5/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/6/website"><img src="https://opencollective.com/miso/organization/6/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/7/website"><img src="https://opencollective.com/miso/organization/7/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/8/website"><img src="https://opencollective.com/miso/organization/8/avatar.svg"></a>+<a href="https://opencollective.com/miso/organization/9/website"><img src="https://opencollective.com/miso/organization/9/avatar.svg"></a>  ## License 
frontend-src/Miso/Html/Internal.hs view
@@ -49,6 +49,7 @@   -- * Life cycle events   , onCreated   , onDestroyed+  , onBeforeDestroyed   -- * Events   , defaultEvents   -- * Subscription type@@ -279,6 +280,19 @@   Attribute $ \sink n -> do     cb <- callbackToJSVal =<< asyncCallback (liftIO (sink action))     set "onDestroyed" cb n+    registerCallback cb++-- | @onBeforeDestroyed action@ is an event that gets called before the DOM element+-- is removed from the DOM. The @action@ is given the DOM element that was+-- removed from the DOM tree.+--+-- Important note: Any node that uses this event MUST have a unique @Key@,+-- otherwise the event may not be reliably called!+onBeforeDestroyed :: action -> Attribute action+onBeforeDestroyed action =+  Attribute $ \sink n -> do+    cb <- callbackToJSVal =<< asyncCallback (liftIO (sink action))+    set "onBeforeDestroyed" cb n     registerCallback cb  -- | @style_ attrs@ is an attribute that will set the @style@
ghc-src/Miso/Html/Internal.hs view
@@ -44,6 +44,7 @@   -- * Life cycle events   , onCreated   , onDestroyed+  , onBeforeDestroyed   ) where  import           Data.Aeson (Value(..), ToJSON(..))@@ -241,6 +242,12 @@ -- removed from the DOM tree. onDestroyed :: action -> Attribute action onDestroyed _ = E ()++-- | @onBeforeDestroyed action@ is an event that gets called before the DOM element+-- is removed from the DOM. The @action@ is given the DOM element that was+-- removed from the DOM tree.+onBeforeDestroyed :: action -> Attribute action+onBeforeDestroyed _ = E ()  -- | Constructs CSS for a DOM Element --
ghcjs-ffi/Miso/FFI.hs view
@@ -32,6 +32,7 @@     , now    , consoleLog+   , consoleLogJSVal    , stringify    , parse    , clearBody@@ -61,7 +62,7 @@    ) where  import           Control.Concurrent-import           Data.Aeson hiding (Object)+import           Data.Aeson                 hiding (Object) import           Data.JSString import           Data.JSString.Int import           Data.JSString.RealFloat@@ -132,7 +133,10 @@  -- | Console-logging foreign import javascript unsafe "console.log($1);"-  consoleLog :: JSVal -> IO ()+  consoleLog :: JSString -> IO ()++foreign import javascript unsafe "console.log($1);"+  consoleLogJSVal :: JSVal -> IO ()  -- | Converts a JS object into a JSON string foreign import javascript unsafe "$r = JSON.stringify($1);"
jsaddle-ffi/Miso/FFI.hs view
@@ -30,6 +30,7 @@     , now    , consoleLog+   , consoleLogJSVal    , stringify    , parse    , clearBody@@ -127,8 +128,14 @@ now = fromJSValUnchecked =<< (jsg "performance" # "now" $ ())  -- | Console-logging-consoleLog :: JSVal -> JSM ()+consoleLog :: JSString -> JSM () consoleLog v = do+  _ <- jsg "console" # "log" $ [toJSString v]+  pure ()++-- | Console-logging+consoleLogJSVal :: JSVal -> JSM ()+consoleLogJSVal v = do   _ <- jsg "console" # "log" $ [v]   pure () 
jsbits/diff.js view
@@ -16,6 +16,7 @@ };  window['destroyNode'] = function destroyNode(obj, parent) {+  window['callBeforeDestroyedRecursive'](obj);   parent.removeChild(obj['domRef']);   window['callDestroyedRecursive'](obj); };@@ -30,6 +31,16 @@   if (obj['onDestroyed']) obj['onDestroyed'](); }; +window['callBeforeDestroyed'] = function callBeforeDestroyed(obj) {+  if (obj['onBeforeDestroyed']) obj['onBeforeDestroyed']();+};++window['callBeforeDestroyedRecursive'] = function callBeforeDestroyedRecursive(obj) {+  window['callBeforeDestroyed'](obj);+  for (var i in obj.children)+    window['callBeforeDestroyedRecursive'](obj.children[i]);+};+ window['diffTextNodes'] = function diffTextNodes(c, n) {   if (c['text'] !== n['text']) c['domRef'].textContent = n['text'];   n['domRef'] = c['domRef'];@@ -37,6 +48,7 @@  window['replaceElementWithText'] = function replaceElementWithText(c, n, parent, doc) {   n['domRef'] = doc.createTextNode(n['text']);+  window['callBeforeDestroyedRecursive'](c);   parent.replaceChild(n['domRef'], c['domRef']);   window['callDestroyedRecursive'](c); };@@ -68,6 +80,7 @@     window['populate'](c, n, doc);   } else {     window['createElement'](n, doc);+    window['callBeforeDestroyedRecursive'](c);     parent.replaceChild(n['domRef'], c['domRef']);     window['callDestroyedRecursive'](c);     window['callCreated'](n);
jsbits/isomorphic.js view
@@ -1,5 +1,5 @@ window = typeof window === 'undefined' ? {} : window;-window['copyDOMIntoVTree'] = function copyDOMIntoVTree(mountPoint, vtree, doc) {+window['copyDOMIntoVTree'] = function copyDOMIntoVTree(mountPoint, vtree, doc = window.document) {   var node = mountPoint ? mountPoint.firstChild : doc.body.firstChild;   if (!window['walk'](vtree, node, doc)) {     console.warn('Could not copy DOM into virtual DOM, falling back to diff');
miso.cabal view
@@ -1,5 +1,5 @@ name:                miso-version:             1.2.0.0+version:             1.3.0.0 category:            Web, Miso, Data Structures license:             BSD3 license-file:        LICENSE
tests/Main.hs view
@@ -102,7 +102,7 @@ runTests :: TestM () -> IO () runTests t = do   results <- toJSVal =<< toResult <$> execStateT t []-  consoleLog results+  consoleLogJSVal results   writeToGlobalObject results     where       toResult :: [Test] -> TestResult