packages feed

shakespeare 2.1.7.1 → 2.2.0

raw patch · 6 files changed

+139/−1 lines, 6 files

Files

ChangeLog.md view
@@ -1,5 +1,46 @@ # ChangeLog for shakespeare +### 2.2.0++* Added $component with binding for reusable blocks++You can now bind a component-producing function and reuse its sub-components within the same Hamlet block.++#### Example++```haskell+data Modal = Modal+  { modalHeader :: Widget -> Widget+  , modalBody   :: Widget -> Widget+  }++modalWidget :: (Modal -> Widget) -> Widget+```++```hamlet+$component modal <- modalWidget+  $component modalHeader modal+    <h1>This is the title++  $component modalBody modal+    <p>This is the content+```++Conceptually, this desugars to:++```haskell+^{ modalWidget $ \modal ->+     [whamlet|+       ^{ modalHeader modal [whamlet| <h1>This is the title |] }+       ^{ modalBody   modal [whamlet| <p>This is the content |] }+     |]+ }+<p>outside+```++Since everything here is just plain Haskell, you can freely pass additional data or parameters to your component-producing function—`modalWidget` and its subcomponents are ordinary functions.+Only the *outermost* component (`modalWidget` in this case) needs to follow the `(Modal -> Widget) -> Widget` pattern; all nested subcomponents can have arbitrary types and arguments.+ ### 2.1.7.1  * Add missing other-messages to shakespeare.cabal [#299](https://github.com/yesodweb/shakespeare/pull/299)
Text/Hamlet.hs view
@@ -216,6 +216,22 @@     inside' <- docToExp env hr scope' (DocWith dis inside)     let lam = LamE [pat] inside'     return $ lam `AppE` deref'+docToExp env hr scope (DocComponent mbinding deref inside) = do+    let deref' = derefToExp scope deref+    case mbinding of+        Just binding -> do+            (pat, extraScope) <- bindingPattern binding+            let scope' = extraScope ++ scope+            inside' <-+                hrWithEnv hr $ \env ->+                    docsToExp env hr scope' inside+            hrEmbed hr env $ deref' `AppE` LamE [pat] inside'+        Nothing -> do+            inside' <-+                hrWithEnv hr $ \env ->+                    docsToExp env hr scope inside+            hrEmbed hr env $ deref' `AppE` inside'+ docToExp env hr scope (DocMaybe val idents inside mno) = do     let val' = derefToExp scope val     (pat, extraScope) <- bindingPattern idents@@ -594,6 +610,7 @@     justContent (DocContent c) = c     justContent DocForall{} = unsupported "$forall"     justContent DocWith{} = unsupported "$with"+    justContent DocComponent{} = unsupported "$component"     justContent DocMaybe{} = unsupported "$maybe"     justContent DocCase{} = unsupported "$case"     justContent DocCond{} = unsupported "attribute conditionals"
Text/Hamlet/Parse.hs view
@@ -66,6 +66,7 @@           | LineNothing           | LineCase Deref           | LineOf Binding+          | LineComponent (Maybe Binding) Deref           | LineTag             { _lineTagName :: String             , _lineAttr :: [(Maybe Deref, String, Maybe [Content])]@@ -125,6 +126,7 @@          controlWith <|>          controlCase <|>          controlOf <|>+         controlComponent <|>          angle <|>          invalidDollar <|>          (eol' >> return (LineContent [] True)) <|>@@ -238,6 +240,14 @@         _   <- spaceTabs         eol         return $ LineOf x+    controlComponent = do+        _ <- try $ string "$component"+        spaces+        (x, b) <- (second Just <$> try binding) <|>+                  ((\x -> (x, Nothing)) <$> parseDeref)+        _ <- spaceTabs+        eol+        return $ LineComponent b x     content cr = do         x <- many $ content' cr         case cr of@@ -450,6 +460,7 @@          | DocCond [(Deref, [Doc])] (Maybe [Doc])          | DocMaybe Deref Binding [Doc] (Maybe [Doc])          | DocCase Deref [(Binding, [Doc])]+         | DocComponent (Maybe Binding) Deref [Doc]          | DocContent Content     deriving (Show, Eq, Read, Data, Typeable) @@ -486,6 +497,10 @@     cases <- mapM getOf inside     rest' <- nestToDoc set rest     Ok $ DocCase d cases : rest'+nestToDoc set (Nest (LineComponent b d) inside:rest) = do+    inside' <- nestToDoc set inside+    rest' <- nestToDoc set rest+    Ok $ DocComponent b d inside' : rest' nestToDoc set (Nest (LineTag tn attrs content classes attrsD avoidNewLine) inside:rest) = do     let attrFix (x, y, z) = (x, y, [(Nothing, z)])     let takeClass (a, "class", b) = Just (a, fromMaybe [] b)@@ -546,6 +561,8 @@     DocForall d i (compressDoc doc) : compressDoc rest compressDoc (DocWith dis doc:rest) =     DocWith dis (compressDoc doc) : compressDoc rest+compressDoc (DocComponent b d i:rest) =+    DocComponent b d i : compressDoc rest compressDoc (DocMaybe d i doc mnothing:rest) =     DocMaybe d i (compressDoc doc) (fmap compressDoc mnothing)   : compressDoc rest
Text/Hamlet/RT.hs view
@@ -112,6 +112,7 @@             docs'' <- mapM convert docs'             return (deref', docs'')     convert DocWith{} = error "Runtime hamlet does not currently support $with"+    convert DocComponent{} = error "Runtime hamlet does not currently support $component"     convert DocCase{} = error "Runtime hamlet does not currently support $case"  renderHamletRT :: MonadThrow m
shakespeare.cabal view
@@ -1,5 +1,5 @@ name:            shakespeare-version:         2.1.7.1+version:         2.2.0 license:         MIT license-file:    LICENSE author:          Michael Snoyman <michael@snoyman.com>
test/Text/HamletSpec.hs view
@@ -22,6 +22,11 @@ import Text.Blaze.Internal (preEscapedString)
 import Text.Blaze
 
+data ExampleModal widget = ExampleModal
+    { modalHeader :: widget -> widget,
+      modalContent :: widget -> widget
+    }
+
 spec = do
     it "empty" caseEmpty
     it "static" caseStatic
@@ -518,6 +523,63 @@                     }"
                 >
             |]
+
+    it "supports $component without binding" $
+        let
+            container :: String -> HtmlUrl url -> HtmlUrl url
+            container clazz x =
+                [hamlet|
+                    $newline never
+                    <div class="container #{clazz}">
+                        ^{x}
+                |]
+        in
+            helper "<div class=\"container alert\"><p>Hello world</p></div><p>outside</p>"
+                [hamlet|
+                    $newline never
+                    $component container "alert"
+                      <p>Hello world
+                    <p>outside
+                |]
+
+    it "supports $component with binding (modal example)" $
+        let
+            modalWidget :: (ExampleModal (HtmlUrl url) -> HtmlUrl url) -> HtmlUrl url
+            modalWidget body =
+              let
+                exampleModal =
+                    ExampleModal
+                      { modalHeader = \content ->
+                            [hamlet|
+                                $newline never
+                                <div class="modal-header">
+                                    ^{content}
+                            |],
+                        modalContent = \content ->
+                            [hamlet|
+                                $newline never
+                                <div class="modal-content">
+                                    ^{content}
+                            |]
+                      }
+              in
+                [hamlet|
+                    $newline never
+                    <div class="modal">
+                        ^{body exampleModal}
+                |]
+        in
+            helper "<div class=\"modal\"><div class=\"modal-header\"><h1>This is the title</h1></div><div class=\"modal-content\"><p>This is the content</p></div></div><p>outside</p>"
+                [hamlet|
+                    $newline never
+                    $component modal <- modalWidget
+                        $component modalHeader modal
+                          <h1>This is the title
+
+                        $component modalContent modal
+                          <p>This is the content
+                    <p>outside
+                |]
 
     it "runtime Hamlet with caret interpolation" $ do
         let toInclude render = render (5, [("hello", "world")])