{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module IHP.View.Form.FormFor where
import IHP.Controller.Context
import IHP.HSX.ConvertibleStrings ()
import IHP.HSX.MarkupQQ (hsx)
import IHP.ModelSupport (Id', InputValue, getModelName, isNew)
import IHP.Prelude
import IHP.View.Form.Fields (hiddenField)
import IHP.View.Types
import IHP.ViewSupport
import Network.Wai (Request, pathInfo)
import IHP.HSX.Markup (Markup, ToHtml(..))
formFor :: forall record. (
?context :: ControllerContext
, ?request :: Request
, ModelFormAction record
, HasField "meta" record MetaBag
) => record -> ((?context :: ControllerContext, ?formContext :: FormContext record) => Markup) -> Markup
formFor :: forall record.
(?context::ControllerContext, ?request::Request,
ModelFormAction record, HasField "meta" record MetaBag) =>
record
-> ((?context::ControllerContext,
?formContext::FormContext record) =>
Markup)
-> Markup
formFor record
record (?context::ControllerContext, ?formContext::FormContext record) =>
Markup
formBody = forall record.
(?context::ControllerContext, ?request::Request,
ModelFormAction record, HasField "meta" record MetaBag) =>
record
-> (FormContext record -> FormContext record)
-> ((?context::ControllerContext,
?formContext::FormContext record) =>
Markup)
-> Markup
formForWithOptions @record record
record (\FormContext record
c -> FormContext record
c) Markup
(?context::ControllerContext, ?formContext::FormContext record) =>
Markup
formBody
{-# INLINE formFor #-}
formForWithOptions :: forall record. (
?context :: ControllerContext
, ?request :: Request
, ModelFormAction record
, HasField "meta" record MetaBag
) => record -> (FormContext record -> FormContext record) -> ((?context :: ControllerContext, ?formContext :: FormContext record) => Markup) -> Markup
formForWithOptions :: forall record.
(?context::ControllerContext, ?request::Request,
ModelFormAction record, HasField "meta" record MetaBag) =>
record
-> (FormContext record -> FormContext record)
-> ((?context::ControllerContext,
?formContext::FormContext record) =>
Markup)
-> Markup
formForWithOptions record
record FormContext record -> FormContext record
applyOptions (?context::ControllerContext, ?formContext::FormContext record) =>
Markup
formBody = FormContext record
-> ((?context::ControllerContext,
?formContext::FormContext record) =>
Markup)
-> Markup
forall model.
(?context::ControllerContext) =>
FormContext model
-> ((?context::ControllerContext,
?formContext::FormContext model) =>
Markup)
-> Markup
buildForm (FormContext record -> FormContext record
applyOptions (record -> FormContext record
forall record.
(?request::Request, HasField "meta" record MetaBag) =>
record -> FormContext record
createFormContext record
record) { formAction = modelFormAction record }) Markup
(?context::ControllerContext, ?formContext::FormContext record) =>
Markup
formBody
{-# INLINE formForWithOptions #-}
formForWithoutJavascript :: forall record. (
?context :: ControllerContext
, ?request :: Request
, ModelFormAction record
, HasField "meta" record MetaBag
) => record -> ((?context :: ControllerContext, ?formContext :: FormContext record) => Markup) -> Markup
formForWithoutJavascript :: forall record.
(?context::ControllerContext, ?request::Request,
ModelFormAction record, HasField "meta" record MetaBag) =>
record
-> ((?context::ControllerContext,
?formContext::FormContext record) =>
Markup)
-> Markup
formForWithoutJavascript record
record (?context::ControllerContext, ?formContext::FormContext record) =>
Markup
formBody = forall record.
(?context::ControllerContext, ?request::Request,
ModelFormAction record, HasField "meta" record MetaBag) =>
record
-> (FormContext record -> FormContext record)
-> ((?context::ControllerContext,
?formContext::FormContext record) =>
Markup)
-> Markup
formForWithOptions @record record
record (\FormContext record
formContext -> FormContext record
formContext { disableJavascriptSubmission = True }) Markup
(?context::ControllerContext, ?formContext::FormContext record) =>
Markup
formBody
{-# INLINE formForWithoutJavascript #-}
formFor' :: forall record. (
?context :: ControllerContext
, ?request :: Request
, HasField "meta" record MetaBag
) => record -> Text -> ((?context :: ControllerContext, ?formContext :: FormContext record) => Markup) -> Markup
formFor' :: forall record.
(?context::ControllerContext, ?request::Request,
HasField "meta" record MetaBag) =>
record
-> Text
-> ((?context::ControllerContext,
?formContext::FormContext record) =>
Markup)
-> Markup
formFor' record
record Text
action = FormContext record
-> ((?context::ControllerContext,
?formContext::FormContext record) =>
Markup)
-> Markup
forall model.
(?context::ControllerContext) =>
FormContext model
-> ((?context::ControllerContext,
?formContext::FormContext model) =>
Markup)
-> Markup
buildForm (record -> FormContext record
forall record.
(?request::Request, HasField "meta" record MetaBag) =>
record -> FormContext record
createFormContext record
record) { formAction = action }
{-# INLINE formFor' #-}
createFormContext :: forall record. (
?request :: Request
, HasField "meta" record MetaBag
) => record -> FormContext record
createFormContext :: forall record.
(?request::Request, HasField "meta" record MetaBag) =>
record -> FormContext record
createFormContext record
record =
FormContext
{ model :: record
model = record
record
, formAction :: Text
formAction = Text
""
, formMethod :: Text
formMethod = Text
"POST"
, cssFramework :: CSSFramework
cssFramework = CSSFramework
(?request::Request) => CSSFramework
theCSSFramework
, formId :: Text
formId = Text
""
, formClass :: Text
formClass = if record -> Bool
forall model. HasField "meta" model MetaBag => model -> Bool
isNew record
record then Text
"new-form" else Text
"edit-form"
, customFormAttributes :: [(Text, Text)]
customFormAttributes = []
, disableJavascriptSubmission :: Bool
disableJavascriptSubmission = Bool
False
, fieldNamePrefix :: Text
fieldNamePrefix = Text
""
}
{-# INLINE createFormContext #-}
buildForm :: forall model. (?context :: ControllerContext) => FormContext model -> ((?context :: ControllerContext, ?formContext :: FormContext model) => Markup) -> Markup
buildForm :: forall model.
(?context::ControllerContext) =>
FormContext model
-> ((?context::ControllerContext,
?formContext::FormContext model) =>
Markup)
-> Markup
buildForm FormContext model
formContext (?context::ControllerContext, ?formContext::FormContext model) =>
Markup
inner = [hsx|
<form
method={formContext.formMethod}
action={formContext.formAction}
id={formContext.formId}
class={formContext.formClass}
data-disable-javascript-submission={formContext.disableJavascriptSubmission}
{...formContext.customFormAttributes}
>
{formInner}
</form>
|]
where
formInner :: Markup
formInner = let ?formContext = ?formContext::FormContext model
FormContext model
formContext in Markup
(?context::ControllerContext, ?formContext::FormContext model) =>
Markup
inner
{-# INLINE buildForm #-}
nestedFormFor :: forall fieldName childRecord parentRecord idType. (
?context :: ControllerContext
, ?formContext :: FormContext parentRecord
, HasField fieldName parentRecord [childRecord]
, KnownSymbol fieldName
, KnownSymbol (GetModelName childRecord)
, HasField "id" childRecord idType
, InputValue idType
, HasField "meta" childRecord MetaBag
) => Proxy fieldName -> ((?context :: ControllerContext, ?formContext :: FormContext childRecord) => Markup) -> Markup
nestedFormFor :: forall (fieldName :: Symbol) childRecord parentRecord idType.
(?context::ControllerContext,
?formContext::FormContext parentRecord,
HasField fieldName parentRecord [childRecord],
KnownSymbol fieldName, KnownSymbol (GetModelName childRecord),
HasField "id" childRecord idType, InputValue idType,
HasField "meta" childRecord MetaBag) =>
Proxy fieldName
-> ((?context::ControllerContext,
?formContext::FormContext childRecord) =>
Markup)
-> Markup
nestedFormFor Proxy fieldName
field (?context::ControllerContext,
?formContext::FormContext childRecord) =>
Markup
nestedRenderForm = [childRecord] -> (Element [childRecord] -> Markup) -> Markup
forall mono (m :: * -> *).
(MonoFoldable mono, Applicative m) =>
mono -> (Element mono -> m ()) -> m ()
forEach [childRecord]
children childRecord -> Markup
Element [childRecord] -> Markup
renderChild
where
parentFormContext :: FormContext parentRecord
parentFormContext :: FormContext parentRecord
parentFormContext = ?formContext::FormContext parentRecord
FormContext parentRecord
?formContext
renderChild :: childRecord -> Markup
renderChild :: childRecord -> Markup
renderChild childRecord
record = let ?formContext = childRecord -> FormContext childRecord
buildNestedFormContext childRecord
record in [hsx|
{hiddenField #id}
{nestedRenderForm}
|]
buildNestedFormContext :: childRecord -> FormContext childRecord
buildNestedFormContext :: childRecord -> FormContext childRecord
buildNestedFormContext childRecord
record = FormContext parentRecord
parentFormContext { model = record, fieldNamePrefix = symbolToText @fieldName <> "_" }
children :: [childRecord]
children :: [childRecord]
children = forall {k} (x :: k) r a. HasField x r a => r -> a
forall (x :: Symbol) r a. HasField x r a => r -> a
getField @fieldName ?formContext::FormContext parentRecord
FormContext parentRecord
?formContext.model
{-# INLINE nestedFormFor #-}
submitButton :: forall model. (?formContext :: FormContext model, HasField "meta" model MetaBag, KnownSymbol (GetModelName model)) => SubmitButton
submitButton :: forall model.
(?formContext::FormContext model, HasField "meta" model MetaBag,
KnownSymbol (GetModelName model)) =>
SubmitButton
submitButton =
let
modelName :: Text
modelName = forall model. KnownSymbol (GetModelName model) => Text
IHP.ModelSupport.getModelName @model
buttonText :: Text
buttonText = Text
modelName Text -> (Text -> Text) -> Text
forall a b. a -> (a -> b) -> b
|> Text -> Text
humanize
isNew :: Bool
isNew = model -> Bool
forall model. HasField "meta" model MetaBag => model -> Bool
IHP.ModelSupport.isNew (FormContext model -> model
forall model. FormContext model -> model
model ?formContext::FormContext model
FormContext model
?formContext)
in SubmitButton
{ label :: Markup
label = Text -> Markup
forall a. ToHtml a => a -> Markup
toHtml (Text -> Markup) -> Text -> Markup
forall a b. (a -> b) -> a -> b
$ (if Bool
isNew then Text
"Create " else Text
"Save ") Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
buttonText
, buttonClass :: Text
buttonClass = Text
forall a. Monoid a => a
mempty
, buttonDisabled :: Bool
buttonDisabled = Bool
False
, cssFramework :: CSSFramework
cssFramework = ?formContext::FormContext model
FormContext model
?formContext.cssFramework
}
{-# INLINE submitButton #-}
class ModelFormAction record where
modelFormAction :: (?context :: ControllerContext, ?request :: Request) => record -> Text
instance
( HasField "id" record (Id' (GetTableName record))
, HasField "meta" record MetaBag
, KnownSymbol (GetModelName record)
, Show (Id' (GetTableName record))
) => ModelFormAction record where
modelFormAction :: (?context::ControllerContext, ?request::Request) => record -> Text
modelFormAction record
record =
let
path :: [Text]
path = Request
(?request::Request) => Request
theRequest.pathInfo
action :: Text
action = if record -> Bool
forall model. HasField "meta" model MetaBag => model -> Bool
isNew record
record
then Text
"Create" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> forall model. KnownSymbol (GetModelName model) => Text
getModelName @record
else Text
"Update" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> forall model. KnownSymbol (GetModelName model) => Text
getModelName @record Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"?" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
lcfirst (forall model. KnownSymbol (GetModelName model) => Text
getModelName @record) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"Id=" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Id' (GetTableName record) -> Text
forall a. Show a => a -> Text
tshow record
record.id
in
[Text] -> Maybe [Text]
forall a. [a] -> Maybe [a]
init [Text]
path
Maybe [Text] -> (Maybe [Text] -> [Text]) -> [Text]
forall a b. a -> (a -> b) -> b
|> (\Maybe [Text]
path -> [Text
""] [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
<> ([Text] -> Maybe [Text] -> [Text]
forall a. a -> Maybe a -> a
fromMaybe [] Maybe [Text]
path) [Text] -> [Text] -> [Text]
forall a. Semigroup a => a -> a -> a
<> [Text
action])
[Text] -> ([Text] -> Text) -> Text
forall a b. a -> (a -> b) -> b
|> Text -> [Text] -> Text
intercalate Text
"/"