backport Yesod.Form.Bootstrap3 to Yesod 1.0.1
This commit is contained in:
parent
54fe9af0bb
commit
84eaf8b447
3 changed files with 88 additions and 5 deletions
|
@ -1,7 +1,10 @@
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE CPP #-}
|
||||||
-- | Helper functions for creating forms when using Bootstrap v3.
|
-- | Helper functions for creating forms when using Bootstrap v3.
|
||||||
|
-- This is a copy of the Yesod.Form.Bootstrap3 module that has been slightly
|
||||||
|
-- modified to be compatible with Yesod 1.0.1
|
||||||
module Assistant.WebApp.Bootstrap3
|
module Assistant.WebApp.Bootstrap3
|
||||||
( -- * Rendering forms
|
( -- * Rendering forms
|
||||||
renderBootstrap3
|
renderBootstrap3
|
||||||
|
@ -146,30 +149,36 @@ data BootstrapFormLayout =
|
||||||
-- > ^{bootstrapSubmit MsgSubmit}
|
-- > ^{bootstrapSubmit MsgSubmit}
|
||||||
--
|
--
|
||||||
-- Since: yesod-form 1.3.8
|
-- Since: yesod-form 1.3.8
|
||||||
|
#if MIN_VERSION_yesod(1,2,0)
|
||||||
renderBootstrap3 :: Monad m => BootstrapFormLayout -> FormRender m a
|
renderBootstrap3 :: Monad m => BootstrapFormLayout -> FormRender m a
|
||||||
|
#else
|
||||||
|
renderBootstrap3 :: BootstrapFormLayout -> FormRender sub master a
|
||||||
|
#endif
|
||||||
renderBootstrap3 formLayout aform fragment = do
|
renderBootstrap3 formLayout aform fragment = do
|
||||||
(res, views') <- aFormToForm aform
|
(res, views') <- aFormToForm aform
|
||||||
let views = views' []
|
let views = views' []
|
||||||
has (Just _) = True
|
has (Just _) = True
|
||||||
has Nothing = False
|
has Nothing = False
|
||||||
widget = [whamlet|
|
widget = [whamlet|
|
||||||
|
#if MIN_VERSION_yesod(1,2,0)
|
||||||
$newline never
|
$newline never
|
||||||
|
#endif
|
||||||
#{fragment}
|
#{fragment}
|
||||||
$forall view <- views
|
$forall view <- views
|
||||||
<div .form-group :fvRequired view:.required :not $ fvRequired view:.optional :has $ fvErrors view:.has-error>
|
<div .form-group :fvRequired view:.required :not $ fvRequired view:.optional :has $ fvErrors view:.has-error>
|
||||||
$case formLayout
|
$case formLayout
|
||||||
$of BootstrapBasicForm
|
$of BootstrapBasicForm
|
||||||
$if fvId view /= bootstrapSubmitId
|
$if nequals (fvId view) bootstrapSubmitId
|
||||||
<label for=#{fvId view}>#{fvLabel view}
|
<label for=#{fvId view}>#{fvLabel view}
|
||||||
^{fvInput view}
|
^{fvInput view}
|
||||||
^{helpWidget view}
|
^{helpWidget view}
|
||||||
$of BootstrapInlineForm
|
$of BootstrapInlineForm
|
||||||
$if fvId view /= bootstrapSubmitId
|
$if nequals (fvId view) bootstrapSubmitId
|
||||||
<label .sr-only for=#{fvId view}>#{fvLabel view}
|
<label .sr-only for=#{fvId view}>#{fvLabel view}
|
||||||
^{fvInput view}
|
^{fvInput view}
|
||||||
^{helpWidget view}
|
^{helpWidget view}
|
||||||
$of BootstrapHorizontalForm labelOffset labelSize inputOffset inputSize
|
$of BootstrapHorizontalForm labelOffset labelSize inputOffset inputSize
|
||||||
$if fvId view /= bootstrapSubmitId
|
$if nequals (fvId view) bootstrapSubmitId
|
||||||
<label .control-label .#{toOffset labelOffset} .#{toColumn labelSize} for=#{fvId view}>#{fvLabel view}
|
<label .control-label .#{toOffset labelOffset} .#{toColumn labelSize} for=#{fvId view}>#{fvLabel view}
|
||||||
<div .#{toOffset inputOffset} .#{toColumn inputSize}>
|
<div .#{toOffset inputOffset} .#{toColumn inputSize}>
|
||||||
^{fvInput view}
|
^{fvInput view}
|
||||||
|
@ -180,10 +189,15 @@ renderBootstrap3 formLayout aform fragment = do
|
||||||
^{helpWidget view}
|
^{helpWidget view}
|
||||||
|]
|
|]
|
||||||
return (res, widget)
|
return (res, widget)
|
||||||
|
where
|
||||||
|
nequals a b = a /= b -- work around older hamlet versions not liking /=
|
||||||
|
|
||||||
-- | (Internal) Render a help widget for tooltips and errors.
|
-- | (Internal) Render a help widget for tooltips and errors.
|
||||||
|
#if MIN_VERSION_yesod(1,2,0)
|
||||||
helpWidget :: FieldView site -> WidgetT site IO ()
|
helpWidget :: FieldView site -> WidgetT site IO ()
|
||||||
|
#else
|
||||||
|
helpWidget :: FieldView sub master -> GWidget sub master ()
|
||||||
|
#endif
|
||||||
helpWidget view = [whamlet|
|
helpWidget view = [whamlet|
|
||||||
$maybe tt <- fvTooltip view
|
$maybe tt <- fvTooltip view
|
||||||
<span .help-block>#{tt}
|
<span .help-block>#{tt}
|
||||||
|
@ -228,9 +242,13 @@ instance IsString msg => IsString (BootstrapSubmit msg) where
|
||||||
-- layout.
|
-- layout.
|
||||||
--
|
--
|
||||||
-- Since: yesod-form 1.3.8
|
-- Since: yesod-form 1.3.8
|
||||||
|
#if MIN_VERSION_yesod(1,2,0)
|
||||||
bootstrapSubmit
|
bootstrapSubmit
|
||||||
:: (RenderMessage site msg, HandlerSite m ~ site, MonadHandler m)
|
:: (RenderMessage site msg, HandlerSite m ~ site, MonadHandler m)
|
||||||
=> BootstrapSubmit msg -> AForm m ()
|
=> BootstrapSubmit msg -> AForm m ()
|
||||||
|
#else
|
||||||
|
bootstrapSubmit :: (RenderMessage master msg) => BootstrapSubmit msg -> AForm sub master ()
|
||||||
|
#endif
|
||||||
bootstrapSubmit = formToAForm . liftM (second return) . mbootstrapSubmit
|
bootstrapSubmit = formToAForm . liftM (second return) . mbootstrapSubmit
|
||||||
|
|
||||||
|
|
||||||
|
@ -239,9 +257,13 @@ bootstrapSubmit = formToAForm . liftM (second return) . mbootstrapSubmit
|
||||||
-- anyway.
|
-- anyway.
|
||||||
--
|
--
|
||||||
-- Since: yesod-form 1.3.8
|
-- Since: yesod-form 1.3.8
|
||||||
|
#if MIN_VERSION_yesod(1,2,0)
|
||||||
mbootstrapSubmit
|
mbootstrapSubmit
|
||||||
:: (RenderMessage site msg, HandlerSite m ~ site, MonadHandler m)
|
:: (RenderMessage site msg, HandlerSite m ~ site, MonadHandler m)
|
||||||
=> BootstrapSubmit msg -> MForm m (FormResult (), FieldView site)
|
=> BootstrapSubmit msg -> MForm m (FormResult (), FieldView site)
|
||||||
|
#else
|
||||||
|
mbootstrapSubmit :: (RenderMessage master msg) => BootstrapSubmit msg -> MForm sub master (FormResult (), FieldView sub master)
|
||||||
|
#endif
|
||||||
mbootstrapSubmit (BootstrapSubmit msg classes attrs) =
|
mbootstrapSubmit (BootstrapSubmit msg classes attrs) =
|
||||||
let res = FormSuccess ()
|
let res = FormSuccess ()
|
||||||
widget = [whamlet|<button class="btn #{classes}" type=submit *{attrs}>_{msg}|]
|
widget = [whamlet|<button class="btn #{classes}" type=submit *{attrs}>_{msg}|]
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
- Licensed under the GNU AGPL version 3 or higher.
|
- Licensed under the GNU AGPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
{-# LANGUAGE CPP #-}
|
||||||
|
|
||||||
module Assistant.WebApp.Common (module X) where
|
module Assistant.WebApp.Common (module X) where
|
||||||
|
|
||||||
import Assistant.Common as X
|
import Assistant.Common as X
|
||||||
|
@ -13,6 +15,9 @@ import Assistant.WebApp.Page as X
|
||||||
import Assistant.WebApp.Form as X
|
import Assistant.WebApp.Form as X
|
||||||
import Assistant.WebApp.Types as X
|
import Assistant.WebApp.Types as X
|
||||||
import Assistant.WebApp.RepoId as X
|
import Assistant.WebApp.RepoId as X
|
||||||
|
#if MIN_VERSION_yesod(1,2,0)
|
||||||
import Utility.Yesod as X hiding (textField, passwordField, insertBy, replace, joinPath, deleteBy, delete, insert, Key, Option)
|
import Utility.Yesod as X hiding (textField, passwordField, insertBy, replace, joinPath, deleteBy, delete, insert, Key, Option)
|
||||||
|
#else
|
||||||
|
import Utility.Yesod as X hiding (textField, passwordField, selectField, selectFieldList, insertBy, replace, joinPath, deleteBy, delete, insert, Key, Option)
|
||||||
|
#endif
|
||||||
import Data.Text as X (Text)
|
import Data.Text as X (Text)
|
||||||
|
|
|
@ -15,12 +15,20 @@ module Assistant.WebApp.Form where
|
||||||
import Assistant.WebApp.Types
|
import Assistant.WebApp.Types
|
||||||
import Assistant.Gpg
|
import Assistant.Gpg
|
||||||
|
|
||||||
|
#if MIN_VERSION_yesod(1,2,0)
|
||||||
import Yesod hiding (textField, passwordField)
|
import Yesod hiding (textField, passwordField)
|
||||||
import Yesod.Form.Fields as F
|
import Yesod.Form.Fields as F
|
||||||
|
#else
|
||||||
|
import Yesod hiding (textField, passwordField, selectField, selectFieldList)
|
||||||
|
import Yesod.Form.Fields as F hiding (selectField, selectFieldList)
|
||||||
|
#endif
|
||||||
import Assistant.WebApp.Bootstrap3 hiding (bfs)
|
import Assistant.WebApp.Bootstrap3 hiding (bfs)
|
||||||
import Data.String (IsString (..))
|
import Data.String (IsString (..))
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
|
||||||
|
import Control.Monad (unless)
|
||||||
|
import Data.Maybe (listToMaybe)
|
||||||
|
|
||||||
{- Yesod's textField sets the required attribute for required fields.
|
{- Yesod's textField sets the required attribute for required fields.
|
||||||
- We don't want this, because many of the forms used in this webapp
|
- We don't want this, because many of the forms used in this webapp
|
||||||
- display a modal dialog when submitted, which interacts badly with
|
- display a modal dialog when submitted, which interacts badly with
|
||||||
|
@ -50,6 +58,54 @@ passwordField = F.passwordField
|
||||||
|]
|
|]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{- In older Yesod versions attrs is written into the <option> tag instead of the
|
||||||
|
- surrounding <select>. This breaks the Bootstrap 3 layout of select fields as
|
||||||
|
- it requires the "form-control" class on the <select> tag.
|
||||||
|
- We need to change that to behave the same way as in newer versions.
|
||||||
|
-}
|
||||||
|
#if ! MIN_VERSION_yesod(1,2,0)
|
||||||
|
selectFieldList :: (Eq a, RenderMessage master FormMessage, RenderMessage master msg) => [(msg, a)] -> Field sub master a
|
||||||
|
selectFieldList = selectField . optionsPairs
|
||||||
|
|
||||||
|
selectField :: (Eq a, RenderMessage master FormMessage) => GHandler sub master (OptionList a) -> Field sub master a
|
||||||
|
selectField = selectFieldHelper
|
||||||
|
(\theId name attrs inside -> [whamlet|<select ##{theId} name=#{name} *{attrs}>^{inside}|]) -- outside
|
||||||
|
(\_theId _name isSel -> [whamlet|<option value=none :isSel:selected>_{MsgSelectNone}|]) -- onOpt
|
||||||
|
(\_theId _name _attrs value isSel text -> [whamlet|<option value=#{value} :isSel:selected>#{text}|]) -- inside
|
||||||
|
|
||||||
|
selectFieldHelper :: (Eq a, RenderMessage master FormMessage)
|
||||||
|
=> (Text -> Text -> [(Text, Text)] -> GWidget sub master () -> GWidget sub master ())
|
||||||
|
-> (Text -> Text -> Bool -> GWidget sub master ())
|
||||||
|
-> (Text -> Text -> [(Text, Text)] -> Text -> Bool -> Text -> GWidget sub master ())
|
||||||
|
-> GHandler sub master (OptionList a) -> Field sub master a
|
||||||
|
selectFieldHelper outside onOpt inside opts' = Field
|
||||||
|
{ fieldParse = \x -> do
|
||||||
|
opts <- opts'
|
||||||
|
return $ selectParser opts x
|
||||||
|
, fieldView = \theId name attrs val isReq -> do
|
||||||
|
opts <- fmap olOptions $ lift opts'
|
||||||
|
outside theId name attrs $ do
|
||||||
|
unless isReq $ onOpt theId name $ not $ render opts val `elem` map optionExternalValue opts
|
||||||
|
flip mapM_ opts $ \opt -> inside
|
||||||
|
theId
|
||||||
|
name
|
||||||
|
((if isReq then (("required", "required"):) else id) attrs)
|
||||||
|
(optionExternalValue opt)
|
||||||
|
((render opts val) == optionExternalValue opt)
|
||||||
|
(optionDisplay opt)
|
||||||
|
}
|
||||||
|
where
|
||||||
|
render _ (Left _) = ""
|
||||||
|
render opts (Right a) = maybe "" optionExternalValue $ listToMaybe $ filter ((== a) . optionInternalValue) opts
|
||||||
|
selectParser _ [] = Right Nothing
|
||||||
|
selectParser opts (s:_) = case s of
|
||||||
|
"" -> Right Nothing
|
||||||
|
"none" -> Right Nothing
|
||||||
|
x -> case olReadExternal opts x of
|
||||||
|
Nothing -> Left $ SomeMessage $ MsgInvalidEntry x
|
||||||
|
Just y -> Right $ Just y
|
||||||
|
#endif
|
||||||
|
|
||||||
{- Makes a note widget be displayed after a field. -}
|
{- Makes a note widget be displayed after a field. -}
|
||||||
#if MIN_VERSION_yesod(1,2,0)
|
#if MIN_VERSION_yesod(1,2,0)
|
||||||
withNote :: (Monad m, ToWidget (HandlerSite m) a) => Field m v -> a -> Field m v
|
withNote :: (Monad m, ToWidget (HandlerSite m) a) => Field m v -> a -> Field m v
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue