{-# LANGUAGE OverloadedStrings #-} module Text.Blaze.Html5.Formlets ( input , textarea , password , hidden , inputInteger , file , checkbox , radio , enumRadio , label , selectHtml , selectRaw , select , Html5Form , Html5Formlet , module Text.Formlets ) where import Data.List (elemIndex) import Text.Formlets hiding (massInput) import Text.Blaze.Html5 ((!)) import qualified Text.Blaze.Html5 as H import qualified Text.Blaze.Html5.Attributes as A import Data.Monoid (mconcat) import Control.Applicative import Control.Applicative.Error type Html5Form m a = Form H.Html m a type Html5Formlet m a = Formlet H.Html m a -- | An input field with an optional value -- input :: Monad m => Html5Formlet m String input = input' $ \n v -> H.input ! A.type_ "text" ! A.name (H.stringValue n) ! A.id (H.stringValue n) ! A.value (H.stringValue v) -- | A textarea with optional rows and columns, and an optional value -- textarea :: Monad m => Maybe Int -> Maybe Int -> Html5Formlet m String textarea r c = input' $ \n v -> (applyAttrs n H.textarea) (H.string v) where applyAttrs n = (! A.name (H.stringValue n)) . rows r . cols c rows = maybe id $ \x -> (! A.rows (H.stringValue $ show x)) cols = maybe id $ \x -> (! A.cols (H.stringValue $ show x)) -- | A password field with an optional value -- password :: Monad m => Html5Formlet m String password = input' $ \n v -> H.input ! A.type_ "password" ! A.name (H.stringValue n) ! A.id (H.stringValue n) ! A.value (H.stringValue v) -- | A hidden input field -- hidden :: Monad m => Html5Formlet m String hidden = input' $ \n v -> H.input ! A.type_ "hidden" ! A.name (H.stringValue n) ! A.id (H.stringValue n) ! A.value (H.stringValue v) -- | A validated integer component -- inputInteger :: Monad m => Html5Formlet m Integer inputInteger x = input (fmap show x) `check` asInteger -- | A file upload form -- file :: Monad m => Html5Form m File file = inputFile $ \n -> H.input ! A.type_ "file" ! A.name (H.stringValue n) ! A.id (H.stringValue n) -- | A checkbox with an optional default value -- checkbox :: Monad m => Html5Formlet m Bool checkbox d = (optionalInput (html d)) `check` asBool where asBool (Just _) = Success True asBool Nothing = Success False html (Just True) n = H.input ! A.type_ "checkbox" ! A.name (H.stringValue n) ! A.id (H.stringValue n) ! A.value "on" ! A.checked "checked" html _ n = H.input ! A.type_ "checkbox" ! A.name (H.stringValue n) ! A.id (H.stringValue n) ! A.value "on" -- | A radio choice -- radio :: Monad m => [(String, String)] -> Html5Formlet m String radio choices = input' $ \n v -> mconcat $ map (makeRadio n v) $ zip choices [1 :: Integer ..] -- todo: validate that the result was in the choices where makeRadio name selected ((value, label'), idx) = do applyAttrs (radio' name value id') H.label ! A.for (H.stringValue id') ! A.class_ "radio" $ H.string label' where applyAttrs | selected == value = (! A.checked "checked") | otherwise = id id' = name ++ "_" ++ show idx radio' n v i = H.input ! A.type_ "radio" ! A.name (H.stringValue n) ! A.id (H.stringValue i) ! A.class_ "radio" ! A.value (H.stringValue v) -- | An radio choice for Enums -- enumRadio :: (Monad m, Enum a) => [(a, String)] -> Html5Formlet m a enumRadio values defaultValue = radio (map toS values) (show . fromEnum <$> defaultValue) `check` convert `check` tryToEnum where toS = fmapFst (show . fromEnum) convert v = maybeRead' v "Conversion error" -- | A label -- label :: Monad m => String -> Form H.Html m () label = xml . H.label . H.string -- | This is a helper function to generate select boxes -- selectHtml :: [(String, H.Html)] -- ^ The values and their labels -> String -- ^ The name -> String -- ^ The value that is selected -> H.Html selectHtml choices name selected = H.select ! A.name (H.stringValue name) $ mconcat $ map makeChoice choices where makeChoice (value, label') = applyAttrs $ H.option ! A.value (H.stringValue value) $ label' where applyAttrs | selected == value = (! A.selected "selected") | otherwise = id -- | A drop-down for selecting values -- selectRaw :: (Monad m) => [(String, H.Html)] -- ^ Pairs of value/label -> Html5Formlet m String -- ^ Resulting formlet selectRaw = input' . selectHtml -- todo: validate that result was in choices -- | A drop-down for anything that is an instance of Eq -- select :: (Eq a, Monad m) => [(a, H.Html)] -- ^ Pairs of value/label -> Html5Formlet m a -- ^ Resulting formlet select ls v = selectRaw (map f $ zip [0 :: Integer ..] ls) selected `check` asInt `check` convert where selected = show <$> (v >>= flip elemIndex (map fst ls)) f (idx, (_,l)) = (show idx, l) convert i | i >= length ls || i < 0 = Failure ["Out of bounds"] | otherwise = Success $ fst $ ls !! i asInt s = maybeRead' s (s ++ " is not a valid int")