/yesod-core/src/Yesod/Core/Widget.hs

https://github.com/yogsototh/yesod · Haskell · 274 lines · 193 code · 34 blank · 47 comment · 0 complexity · 526b521f25e2e3b9c35411e660347ade MD5 · raw file

  1. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  2. {-# LANGUAGE TupleSections #-}
  3. {-# LANGUAGE RankNTypes #-}
  4. {-# LANGUAGE FlexibleContexts #-}
  5. {-# LANGUAGE FlexibleInstances #-}
  6. {-# LANGUAGE TypeFamilies #-}
  7. {-# LANGUAGE TemplateHaskell #-}
  8. {-# LANGUAGE MultiParamTypeClasses #-}
  9. {-# LANGUAGE TypeSynonymInstances #-}
  10. {-# LANGUAGE UndecidableInstances #-}
  11. -- | Widgets combine HTML with JS and CSS dependencies with a unique identifier
  12. -- generator, allowing you to create truly modular HTML components.
  13. module Yesod.Core.Widget
  14. ( -- * Datatype
  15. WidgetT
  16. , WidgetFor
  17. , PageContent (..)
  18. -- * Special Hamlet quasiquoter/TH for Widgets
  19. , whamlet
  20. , whamletFile
  21. , ihamletToRepHtml
  22. , ihamletToHtml
  23. -- * Convert to Widget
  24. , ToWidget (..)
  25. , ToWidgetHead (..)
  26. , ToWidgetBody (..)
  27. , ToWidgetMedia (..)
  28. -- * Creating
  29. -- ** Head of page
  30. , setTitle
  31. , setTitleI
  32. -- ** CSS
  33. , addStylesheet
  34. , addStylesheetAttrs
  35. , addStylesheetRemote
  36. , addStylesheetRemoteAttrs
  37. , addStylesheetEither
  38. , CssBuilder (..)
  39. -- ** Javascript
  40. , addScript
  41. , addScriptAttrs
  42. , addScriptRemote
  43. , addScriptRemoteAttrs
  44. , addScriptEither
  45. -- * Subsites
  46. , handlerToWidget
  47. -- * Internal
  48. , whamletFileWithSettings
  49. , asWidgetT
  50. ) where
  51. import Data.Monoid
  52. import qualified Text.Blaze.Html5 as H
  53. import Text.Hamlet
  54. import Text.Cassius
  55. import Text.Julius
  56. import Yesod.Routes.Class
  57. import Yesod.Core.Handler (getMessageRender, getUrlRenderParams)
  58. import Text.Shakespeare.I18N (RenderMessage)
  59. import Data.Text (Text)
  60. import qualified Data.Map as Map
  61. import Language.Haskell.TH.Quote (QuasiQuoter)
  62. import Language.Haskell.TH.Syntax (Q, Exp (InfixE, VarE, LamE, AppE), Pat (VarP), newName)
  63. import qualified Text.Hamlet as NP
  64. import Data.Text.Lazy.Builder (fromLazyText)
  65. import Text.Blaze.Html (toHtml, preEscapedToMarkup)
  66. import qualified Data.Text.Lazy as TL
  67. import qualified Data.Text.Lazy.Builder as TB
  68. import Yesod.Core.Types
  69. import Yesod.Core.Class.Handler
  70. type WidgetT site (m :: * -> *) = WidgetFor site
  71. {-# DEPRECATED WidgetT "Use WidgetFor directly" #-}
  72. preEscapedLazyText :: TL.Text -> Html
  73. preEscapedLazyText = preEscapedToMarkup
  74. class ToWidget site a where
  75. toWidget :: (MonadWidget m, HandlerSite m ~ site) => a -> m ()
  76. instance render ~ RY site => ToWidget site (render -> Html) where
  77. toWidget x = tell $ GWData (Body x) mempty mempty mempty mempty mempty mempty
  78. instance render ~ RY site => ToWidget site (render -> Css) where
  79. toWidget x = toWidget $ CssBuilder . fromLazyText . renderCss . x
  80. instance ToWidget site Css where
  81. toWidget x = toWidget $ CssBuilder . fromLazyText . renderCss . const x
  82. instance render ~ RY site => ToWidget site (render -> CssBuilder) where
  83. toWidget x = tell $ GWData mempty mempty mempty mempty (Map.singleton Nothing $ unCssBuilder . x) mempty mempty
  84. instance ToWidget site CssBuilder where
  85. toWidget x = tell $ GWData mempty mempty mempty mempty (Map.singleton Nothing $ unCssBuilder . const x) mempty mempty
  86. instance render ~ RY site => ToWidget site (render -> Javascript) where
  87. toWidget x = tell $ GWData mempty mempty mempty mempty mempty (Just x) mempty
  88. instance ToWidget site Javascript where
  89. toWidget x = tell $ GWData mempty mempty mempty mempty mempty (Just $ const x) mempty
  90. instance (site' ~ site, a ~ ()) => ToWidget site' (WidgetFor site a) where
  91. toWidget = liftWidget
  92. instance ToWidget site Html where
  93. toWidget = toWidget . const
  94. -- | @since 1.4.28
  95. instance ToWidget site Text where
  96. toWidget = toWidget . toHtml
  97. -- | @since 1.4.28
  98. instance ToWidget site TL.Text where
  99. toWidget = toWidget . toHtml
  100. -- | @since 1.4.28
  101. instance ToWidget site TB.Builder where
  102. toWidget = toWidget . toHtml
  103. -- | Allows adding some CSS to the page with a specific media type.
  104. --
  105. -- Since 1.2
  106. class ToWidgetMedia site a where
  107. -- | Add the given content to the page, but only for the given media type.
  108. --
  109. -- Since 1.2
  110. toWidgetMedia :: (MonadWidget m, HandlerSite m ~ site)
  111. => Text -- ^ media value
  112. -> a
  113. -> m ()
  114. instance render ~ RY site => ToWidgetMedia site (render -> Css) where
  115. toWidgetMedia media x = toWidgetMedia media $ CssBuilder . fromLazyText . renderCss . x
  116. instance ToWidgetMedia site Css where
  117. toWidgetMedia media x = toWidgetMedia media $ CssBuilder . fromLazyText . renderCss . const x
  118. instance render ~ RY site => ToWidgetMedia site (render -> CssBuilder) where
  119. toWidgetMedia media x = tell $ GWData mempty mempty mempty mempty (Map.singleton (Just media) $ unCssBuilder . x) mempty mempty
  120. instance ToWidgetMedia site CssBuilder where
  121. toWidgetMedia media x = tell $ GWData mempty mempty mempty mempty (Map.singleton (Just media) $ unCssBuilder . const x) mempty mempty
  122. class ToWidgetBody site a where
  123. toWidgetBody :: (MonadWidget m, HandlerSite m ~ site) => a -> m ()
  124. instance render ~ RY site => ToWidgetBody site (render -> Html) where
  125. toWidgetBody = toWidget
  126. instance render ~ RY site => ToWidgetBody site (render -> Javascript) where
  127. toWidgetBody j = toWidget $ \r -> H.script $ preEscapedLazyText $ renderJavascriptUrl r j
  128. instance ToWidgetBody site Javascript where
  129. toWidgetBody j = toWidget $ \_ -> H.script $ preEscapedLazyText $ renderJavascript j
  130. instance ToWidgetBody site Html where
  131. toWidgetBody = toWidget
  132. class ToWidgetHead site a where
  133. toWidgetHead :: (MonadWidget m, HandlerSite m ~ site) => a -> m ()
  134. instance render ~ RY site => ToWidgetHead site (render -> Html) where
  135. toWidgetHead = tell . GWData mempty mempty mempty mempty mempty mempty . Head
  136. instance render ~ RY site => ToWidgetHead site (render -> Css) where
  137. toWidgetHead = toWidget
  138. instance ToWidgetHead site Css where
  139. toWidgetHead = toWidget
  140. instance render ~ RY site => ToWidgetHead site (render -> CssBuilder) where
  141. toWidgetHead = toWidget
  142. instance ToWidgetHead site CssBuilder where
  143. toWidgetHead = toWidget
  144. instance render ~ RY site => ToWidgetHead site (render -> Javascript) where
  145. toWidgetHead j = toWidgetHead $ \r -> H.script $ preEscapedLazyText $ renderJavascriptUrl r j
  146. instance ToWidgetHead site Javascript where
  147. toWidgetHead j = toWidgetHead $ \_ -> H.script $ preEscapedLazyText $ renderJavascript j
  148. instance ToWidgetHead site Html where
  149. toWidgetHead = toWidgetHead . const
  150. -- | Set the page title. Calling 'setTitle' multiple times overrides previously
  151. -- set values.
  152. setTitle :: MonadWidget m => Html -> m ()
  153. setTitle x = tell $ GWData mempty (Last $ Just $ Title x) mempty mempty mempty mempty mempty
  154. -- | Set the page title. Calling 'setTitle' multiple times overrides previously
  155. -- set values.
  156. setTitleI :: (MonadWidget m, RenderMessage (HandlerSite m) msg) => msg -> m ()
  157. setTitleI msg = do
  158. mr <- getMessageRender
  159. setTitle $ toHtml $ mr msg
  160. -- | Link to the specified local stylesheet.
  161. addStylesheet :: MonadWidget m => Route (HandlerSite m) -> m ()
  162. addStylesheet = flip addStylesheetAttrs []
  163. -- | Link to the specified local stylesheet.
  164. addStylesheetAttrs :: MonadWidget m
  165. => Route (HandlerSite m)
  166. -> [(Text, Text)]
  167. -> m ()
  168. addStylesheetAttrs x y = tell $ GWData mempty mempty mempty (toUnique $ Stylesheet (Local x) y) mempty mempty mempty
  169. -- | Link to the specified remote stylesheet.
  170. addStylesheetRemote :: MonadWidget m => Text -> m ()
  171. addStylesheetRemote = flip addStylesheetRemoteAttrs []
  172. -- | Link to the specified remote stylesheet.
  173. addStylesheetRemoteAttrs :: MonadWidget m => Text -> [(Text, Text)] -> m ()
  174. addStylesheetRemoteAttrs x y = tell $ GWData mempty mempty mempty (toUnique $ Stylesheet (Remote x) y) mempty mempty mempty
  175. addStylesheetEither :: MonadWidget m
  176. => Either (Route (HandlerSite m)) Text
  177. -> m ()
  178. addStylesheetEither = either addStylesheet addStylesheetRemote
  179. addScriptEither :: MonadWidget m
  180. => Either (Route (HandlerSite m)) Text
  181. -> m ()
  182. addScriptEither = either addScript addScriptRemote
  183. -- | Link to the specified local script.
  184. addScript :: MonadWidget m => Route (HandlerSite m) -> m ()
  185. addScript = flip addScriptAttrs []
  186. -- | Link to the specified local script.
  187. addScriptAttrs :: MonadWidget m => Route (HandlerSite m) -> [(Text, Text)] -> m ()
  188. addScriptAttrs x y = tell $ GWData mempty mempty (toUnique $ Script (Local x) y) mempty mempty mempty mempty
  189. -- | Link to the specified remote script.
  190. addScriptRemote :: MonadWidget m => Text -> m ()
  191. addScriptRemote = flip addScriptRemoteAttrs []
  192. -- | Link to the specified remote script.
  193. addScriptRemoteAttrs :: MonadWidget m => Text -> [(Text, Text)] -> m ()
  194. addScriptRemoteAttrs x y = tell $ GWData mempty mempty (toUnique $ Script (Remote x) y) mempty mempty mempty mempty
  195. whamlet :: QuasiQuoter
  196. whamlet = NP.hamletWithSettings rules NP.defaultHamletSettings
  197. whamletFile :: FilePath -> Q Exp
  198. whamletFile = NP.hamletFileWithSettings rules NP.defaultHamletSettings
  199. whamletFileWithSettings :: NP.HamletSettings -> FilePath -> Q Exp
  200. whamletFileWithSettings = NP.hamletFileWithSettings rules
  201. asWidgetT :: WidgetT site m () -> WidgetT site m ()
  202. asWidgetT = id
  203. rules :: Q NP.HamletRules
  204. rules = do
  205. ah <- [|asWidgetT . toWidget|]
  206. let helper qg f = do
  207. x <- newName "urender"
  208. e <- f $ VarE x
  209. let e' = LamE [VarP x] e
  210. g <- qg
  211. bind <- [|(>>=)|]
  212. return $ InfixE (Just g) bind (Just e')
  213. let ur f = do
  214. let env = NP.Env
  215. (Just $ helper [|getUrlRenderParams|])
  216. (Just $ helper [|fmap (toHtml .) getMessageRender|])
  217. f env
  218. return $ NP.HamletRules ah ur $ \_ b -> return $ ah `AppE` b
  219. -- | Wraps the 'Content' generated by 'hamletToContent' in a 'RepHtml'.
  220. ihamletToRepHtml :: (MonadHandler m, RenderMessage (HandlerSite m) message)
  221. => HtmlUrlI18n message (Route (HandlerSite m))
  222. -> m Html
  223. ihamletToRepHtml = ihamletToHtml
  224. {-# DEPRECATED ihamletToRepHtml "Please use ihamletToHtml instead" #-}
  225. -- | Wraps the 'Content' generated by 'hamletToContent' in a 'RepHtml'.
  226. --
  227. -- Since 1.2.1
  228. ihamletToHtml :: (MonadHandler m, RenderMessage (HandlerSite m) message)
  229. => HtmlUrlI18n message (Route (HandlerSite m))
  230. -> m Html
  231. ihamletToHtml ih = do
  232. urender <- getUrlRenderParams
  233. mrender <- getMessageRender
  234. return $ ih (toHtml . mrender) urender
  235. tell :: MonadWidget m => GWData (Route (HandlerSite m)) -> m ()
  236. tell = liftWidget . tellWidget
  237. toUnique :: x -> UniqueList x
  238. toUnique = UniqueList . (:)
  239. handlerToWidget :: HandlerFor site a -> WidgetFor site a
  240. handlerToWidget (HandlerFor f) = WidgetFor $ f . wdHandler