mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-12-27 02:52:22 +03:00
Implemented parsing and generation of entity-list field render function. (#84)
This commit is contained in:
parent
c695f30704
commit
f955fa6a8d
@ -49,6 +49,14 @@ export class {= listName =} extends React.Component {
|
|||||||
this.setState({ {= entityBeingEditedStateVar =}: null })
|
this.setState({ {= entityBeingEditedStateVar =}: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{=! Render "render" functions for each field, if provided =}
|
||||||
|
{=# listFields =}
|
||||||
|
{=# render =}
|
||||||
|
{= renderFnName =} =
|
||||||
|
{=& render =}
|
||||||
|
{=/ render =}
|
||||||
|
{=/ listFields =}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {= entityLowerName =}ListToShow = this.props.filter ?
|
const {= entityLowerName =}ListToShow = this.props.filter ?
|
||||||
{=! TODO(matija): duplication, we could extract entityLowerName_List =}
|
{=! TODO(matija): duplication, we could extract entityLowerName_List =}
|
||||||
@ -104,7 +112,12 @@ export class {= listName =} extends React.Component {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
{=# render =}
|
||||||
|
this.{= renderFnName =}({= entityLowerName =})
|
||||||
|
{=/ render =}
|
||||||
|
{=^ render =}
|
||||||
{= entityLowerName =}.{= name =}
|
{= entityLowerName =}.{= name =}
|
||||||
|
{=/ render =}
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</ClickAwayListener>
|
</ClickAwayListener>
|
||||||
|
@ -68,5 +68,18 @@ entity-form<Task> NewTaskForm {
|
|||||||
|
|
||||||
// Entity list definition.
|
// Entity list definition.
|
||||||
entity-list<Task> TaskList {
|
entity-list<Task> TaskList {
|
||||||
// Options TBD, not supported for now.
|
fields: {
|
||||||
|
description: {
|
||||||
|
// The contract for render is: user must provide a function that:
|
||||||
|
// - receives a task as an input
|
||||||
|
// - returns a React Node or something that can be rendered by JSX
|
||||||
|
// - does not depend on any outer context
|
||||||
|
render: {=js
|
||||||
|
(task) => {
|
||||||
|
if (task.isDone) return (<s>{task.description}</s>)
|
||||||
|
return task.description
|
||||||
|
}
|
||||||
|
js=}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,10 +105,12 @@ entity-list<Task> TaskList {
|
|||||||
// - receives a task as an input
|
// - receives a task as an input
|
||||||
// - returns a React Node or something that can be rendered by JSX
|
// - returns a React Node or something that can be rendered by JSX
|
||||||
// - does not depend on any outer context
|
// - does not depend on any outer context
|
||||||
render: (task) => {
|
render: {=js
|
||||||
if (task.isDone) return (<s>task.description</s>)
|
(task) => {
|
||||||
|
if (task.isDone) return (<s>{task.description}</s>)
|
||||||
return task.description
|
return task.description
|
||||||
}
|
}
|
||||||
|
js=}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,18 @@ import Data.Maybe (fromJust)
|
|||||||
import Path ((</>), reldir, relfile, parseRelFile)
|
import Path ((</>), reldir, relfile, parseRelFile)
|
||||||
import qualified Path.Aliases as Path
|
import qualified Path.Aliases as Path
|
||||||
|
|
||||||
|
import qualified Util as U
|
||||||
|
|
||||||
import qualified Wasp
|
import qualified Wasp
|
||||||
import Wasp (Wasp)
|
import Wasp (Wasp)
|
||||||
import qualified Wasp.EntityList as WEL
|
import qualified Wasp.EntityList as WEL
|
||||||
|
import qualified Wasp.JsCode
|
||||||
|
|
||||||
import qualified Generator.FileDraft as FD
|
import qualified Generator.FileDraft as FD
|
||||||
import qualified Generator.Entity.Common as EC
|
import qualified Generator.Entity.Common as EC
|
||||||
import qualified Generator.Common as Common
|
import qualified Generator.Common as Common
|
||||||
|
|
||||||
|
|
||||||
data EntityListTemplateData = EntityListTemplateData
|
data EntityListTemplateData = EntityListTemplateData
|
||||||
{ _listName :: !String
|
{ _listName :: !String
|
||||||
, _entityName :: !String
|
, _entityName :: !String
|
||||||
@ -40,6 +44,8 @@ instance ToJSON EntityListTemplateData where
|
|||||||
data ListFieldTemplateData = ListFieldTemplateData
|
data ListFieldTemplateData = ListFieldTemplateData
|
||||||
{ _fieldName :: !String
|
{ _fieldName :: !String
|
||||||
, _fieldType :: !Wasp.EntityFieldType
|
, _fieldType :: !Wasp.EntityFieldType
|
||||||
|
, _fieldRender :: Maybe Wasp.JsCode.JsCode
|
||||||
|
, _fieldRenderFnName :: String
|
||||||
}
|
}
|
||||||
|
|
||||||
instance ToJSON ListFieldTemplateData where
|
instance ToJSON ListFieldTemplateData where
|
||||||
@ -47,6 +53,8 @@ instance ToJSON ListFieldTemplateData where
|
|||||||
object
|
object
|
||||||
[ "name" .= _fieldName f
|
[ "name" .= _fieldName f
|
||||||
, "type" .= _fieldType f
|
, "type" .= _fieldType f
|
||||||
|
, "render" .= _fieldRender f
|
||||||
|
, "renderFnName" .= _fieldRenderFnName f
|
||||||
]
|
]
|
||||||
|
|
||||||
createEntityListTemplateData :: Wasp.Entity -> WEL.EntityList -> EntityListTemplateData
|
createEntityListTemplateData :: Wasp.Entity -> WEL.EntityList -> EntityListTemplateData
|
||||||
@ -58,17 +66,26 @@ createEntityListTemplateData entity entityList =
|
|||||||
, _entityName = Wasp.entityName entity
|
, _entityName = Wasp.entityName entity
|
||||||
, _entityClassName = EC.getEntityClassName entity
|
, _entityClassName = EC.getEntityClassName entity
|
||||||
, _entityLowerName = EC.getEntityLowerName entity
|
, _entityLowerName = EC.getEntityLowerName entity
|
||||||
, _listFields = map (createListFieldTD entityList) $ Wasp.entityFields entity
|
, _listFields = map (createListFieldTD entity entityList) $ Wasp.entityFields entity
|
||||||
, _entityBeingEditedStateVar = entityLowerName ++ "BeingEdited"
|
, _entityBeingEditedStateVar = entityLowerName ++ "BeingEdited"
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
entityLowerName = EC.getEntityLowerName entity
|
entityLowerName = EC.getEntityLowerName entity
|
||||||
|
|
||||||
createListFieldTD :: WEL.EntityList -> Wasp.EntityField -> ListFieldTemplateData
|
createListFieldTD :: Wasp.Entity -> WEL.EntityList -> Wasp.EntityField -> ListFieldTemplateData
|
||||||
createListFieldTD _ entityField = ListFieldTemplateData
|
createListFieldTD entity entityList entityField = ListFieldTemplateData
|
||||||
{ _fieldName = Wasp.entityFieldName entityField
|
{ _fieldName = Wasp.entityFieldName entityField
|
||||||
, _fieldType = Wasp.entityFieldType entityField
|
, _fieldType = Wasp.entityFieldType entityField
|
||||||
|
, _fieldRender = listFieldConfig >>= WEL._fieldRender
|
||||||
|
, _fieldRenderFnName = "render" ++ entityUpper ++ entityFieldUpper
|
||||||
}
|
}
|
||||||
|
where
|
||||||
|
-- Configuration of a form field within entity-list, if there is any.
|
||||||
|
listFieldConfig :: Maybe WEL.Field
|
||||||
|
listFieldConfig = WEL.getConfigForField entityList entityField
|
||||||
|
|
||||||
|
entityUpper = U.toUpperFirst $ Wasp.entityName entity
|
||||||
|
entityFieldUpper = U.toUpperFirst $ Wasp.entityFieldName entityField
|
||||||
|
|
||||||
generateEntityList :: Wasp -> WEL.EntityList -> FD.FileDraft
|
generateEntityList :: Wasp -> WEL.EntityList -> FD.FileDraft
|
||||||
generateEntityList wasp entityList =
|
generateEntityList wasp entityList =
|
||||||
|
@ -12,8 +12,8 @@ import Lexer
|
|||||||
import Parser.App (app)
|
import Parser.App (app)
|
||||||
import Parser.Page (page)
|
import Parser.Page (page)
|
||||||
import Parser.Entity (entity)
|
import Parser.Entity (entity)
|
||||||
import Parser.EntityForm (entityForm)
|
import Parser.Entity.EntityForm (entityForm)
|
||||||
import Parser.EntityList (entityList)
|
import Parser.Entity.EntityList (entityList)
|
||||||
import Parser.JsImport (jsImport)
|
import Parser.JsImport (jsImport)
|
||||||
import Parser.Common (runWaspParser)
|
import Parser.Common (runWaspParser)
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import qualified Path.Aliases as Path
|
|||||||
|
|
||||||
import qualified Lexer as L
|
import qualified Lexer as L
|
||||||
|
|
||||||
|
|
||||||
-- | Runs given wasp parser on a specified input.
|
-- | Runs given wasp parser on a specified input.
|
||||||
runWaspParser :: Parser a -> String -> Either ParseError a
|
runWaspParser :: Parser a -> String -> Either ParseError a
|
||||||
runWaspParser waspParser input = parse waspParser sourceName input
|
runWaspParser waspParser input = parse waspParser sourceName input
|
||||||
|
39
src/Parser/Entity/Common.hs
Normal file
39
src/Parser/Entity/Common.hs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
module Parser.Entity.Common
|
||||||
|
( waspPropertyEntityFields
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Text.Parsec.String (Parser)
|
||||||
|
|
||||||
|
import qualified Lexer as L
|
||||||
|
import Parser.Common as P
|
||||||
|
|
||||||
|
-- A function that takes an entity field name (e.g. "description) and a list of parsed field
|
||||||
|
-- options, and then creates a final Wasp AST record from it (fieldConfig).
|
||||||
|
type CreateFieldConfig fieldOption fieldConfig = (String, [fieldOption]) -> fieldConfig
|
||||||
|
|
||||||
|
-- | Parses configuration of fields within a wasp entity component (e.g. entity-form
|
||||||
|
-- or entity-list). Parses the following format:
|
||||||
|
--
|
||||||
|
-- fields: { FIELD_NAME: {...}, FIELD_NAME: {...}, ... }
|
||||||
|
--
|
||||||
|
-- At least one field must be specified.
|
||||||
|
waspPropertyEntityFields
|
||||||
|
:: Parser fo -- ^ Parser of a single field option.
|
||||||
|
-> CreateFieldConfig fo fc -- ^ Function that creates a record with all parsed field options.
|
||||||
|
-> Parser [fc] -- ^ Field configs, a list of record with all the field options.
|
||||||
|
waspPropertyEntityFields fieldOptionP createFieldConfig = P.waspPropertyClosure "fields" $
|
||||||
|
L.commaSep1 $ waspPropertyEntityField fieldOptionP createFieldConfig
|
||||||
|
|
||||||
|
|
||||||
|
-- | Parses configuration of a specific field within a wasp entity component (e.g. entity-form
|
||||||
|
-- or entity-list). Parses the following format:
|
||||||
|
--
|
||||||
|
-- FIELD_NAME: { option1, option2 }
|
||||||
|
--
|
||||||
|
-- At least one option must be present.
|
||||||
|
waspPropertyEntityField
|
||||||
|
:: Parser fo -- ^ Parser of a single field option.
|
||||||
|
-> CreateFieldConfig fo fc -- ^ Function that creates a record with all parsed field options.
|
||||||
|
-> Parser fc -- ^ Field config, a record with all the field options.
|
||||||
|
waspPropertyEntityField fieldOptionP createFieldConfig =
|
||||||
|
(P.waspIdentifierClosure $ L.commaSep1 fieldOptionP) >>= (return . createFieldConfig)
|
@ -1,4 +1,4 @@
|
|||||||
module Parser.EntityForm
|
module Parser.Entity.EntityForm
|
||||||
( entityForm
|
( entityForm
|
||||||
|
|
||||||
-- For testing
|
-- For testing
|
||||||
@ -11,10 +11,11 @@ module Parser.EntityForm
|
|||||||
import Text.Parsec (choice)
|
import Text.Parsec (choice)
|
||||||
import Text.Parsec.String (Parser)
|
import Text.Parsec.String (Parser)
|
||||||
|
|
||||||
import qualified Wasp.EntityForm as EF
|
import qualified Wasp.EntityForm as WEF
|
||||||
import Wasp.EntityForm (EntityForm)
|
import Wasp.EntityForm (EntityForm)
|
||||||
|
|
||||||
import qualified Parser.Common as P
|
import qualified Parser.Common as P
|
||||||
|
import qualified Parser.Entity.Common as PE
|
||||||
import qualified Util as U
|
import qualified Util as U
|
||||||
import qualified Lexer as L
|
import qualified Lexer as L
|
||||||
|
|
||||||
@ -27,16 +28,16 @@ entityForm = do
|
|||||||
(entityName, formName, options) <-
|
(entityName, formName, options) <-
|
||||||
P.waspElementLinkedToEntity L.reservedNameEntityForm entityFormOptions
|
P.waspElementLinkedToEntity L.reservedNameEntityForm entityFormOptions
|
||||||
|
|
||||||
return EF.EntityForm
|
return WEF.EntityForm
|
||||||
{ EF._name = formName
|
{ WEF._name = formName
|
||||||
, EF._entityName = entityName
|
, WEF._entityName = entityName
|
||||||
, EF._submit = maybeGetSubmitConfig options
|
, WEF._submit = maybeGetSubmitConfig options
|
||||||
, EF._fields = getFieldsConfig options
|
, WEF._fields = getFieldsConfig options
|
||||||
}
|
}
|
||||||
|
|
||||||
data EntityFormOption
|
data EntityFormOption
|
||||||
= EfoSubmit EF.Submit
|
= EfoSubmit WEF.Submit
|
||||||
| EfoFields [EF.Field]
|
| EfoFields [WEF.Field]
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
entityFormOptions :: Parser [EntityFormOption]
|
entityFormOptions :: Parser [EntityFormOption]
|
||||||
@ -50,25 +51,25 @@ entityFormOption = choice
|
|||||||
|
|
||||||
-- * Submit
|
-- * Submit
|
||||||
|
|
||||||
maybeGetSubmitConfig :: [EntityFormOption] -> Maybe EF.Submit
|
maybeGetSubmitConfig :: [EntityFormOption] -> Maybe WEF.Submit
|
||||||
maybeGetSubmitConfig options = U.headSafe [s | EfoSubmit s <- options]
|
maybeGetSubmitConfig options = U.headSafe [s | EfoSubmit s <- options]
|
||||||
|
|
||||||
entityFormOptionSubmit :: Parser EntityFormOption
|
entityFormOptionSubmit :: Parser EntityFormOption
|
||||||
entityFormOptionSubmit = EfoSubmit <$> (P.waspPropertyClosure "submit" submitConfig)
|
entityFormOptionSubmit = EfoSubmit <$> (P.waspPropertyClosure "submit" submitConfig)
|
||||||
|
|
||||||
submitConfig :: Parser EF.Submit
|
submitConfig :: Parser WEF.Submit
|
||||||
submitConfig = do
|
submitConfig = do
|
||||||
-- TODO(matija): this pattern of "having at least 1 property in closure" could be further
|
-- TODO(matija): this pattern of "having at least 1 property in closure" could be further
|
||||||
-- extracted to e.g. "waspClosureOptions" - but again sometimes it is ok not to have any props,
|
-- extracted to e.g. "waspClosureOptions" - but again sometimes it is ok not to have any props,
|
||||||
-- e.g. EntityForm. Maybe then "waspClosureOptions1" and "waspClosureOptions"?
|
-- e.g. EntityForm. Maybe then "waspClosureOptions1" and "waspClosureOptions"?
|
||||||
options <- L.commaSep1 submitOption
|
options <- L.commaSep1 submitOption
|
||||||
|
|
||||||
return EF.Submit
|
return WEF.Submit
|
||||||
{ EF._onEnter = maybeGetSoOnEnter options
|
{ WEF._onEnter = maybeGetSoOnEnter options
|
||||||
, EF._submitButton = maybeGetSoSubmitButton options
|
, WEF._submitButton = maybeGetSoSubmitButton options
|
||||||
}
|
}
|
||||||
|
|
||||||
data SubmitOption = SoOnEnter Bool | SoSubmitButton EF.SubmitButton deriving (Show, Eq)
|
data SubmitOption = SoOnEnter Bool | SoSubmitButton WEF.SubmitButton deriving (Show, Eq)
|
||||||
|
|
||||||
submitOption :: Parser SubmitOption
|
submitOption :: Parser SubmitOption
|
||||||
submitOption = choice [submitOptionOnEnter, submitOptionSubmitButton]
|
submitOption = choice [submitOptionOnEnter, submitOptionSubmitButton]
|
||||||
@ -84,15 +85,15 @@ maybeGetSoOnEnter options = U.headSafe [b | SoOnEnter b <- options]
|
|||||||
submitOptionSubmitButton :: Parser SubmitOption
|
submitOptionSubmitButton :: Parser SubmitOption
|
||||||
submitOptionSubmitButton = SoSubmitButton <$> P.waspPropertyClosure "button" submitButtonConfig
|
submitOptionSubmitButton = SoSubmitButton <$> P.waspPropertyClosure "button" submitButtonConfig
|
||||||
|
|
||||||
maybeGetSoSubmitButton :: [SubmitOption] -> Maybe EF.SubmitButton
|
maybeGetSoSubmitButton :: [SubmitOption] -> Maybe WEF.SubmitButton
|
||||||
maybeGetSoSubmitButton options = U.headSafe [sb | SoSubmitButton sb <- options]
|
maybeGetSoSubmitButton options = U.headSafe [sb | SoSubmitButton sb <- options]
|
||||||
|
|
||||||
submitButtonConfig :: Parser EF.SubmitButton
|
submitButtonConfig :: Parser WEF.SubmitButton
|
||||||
submitButtonConfig = do
|
submitButtonConfig = do
|
||||||
options <- L.commaSep1 submitButtonOption
|
options <- L.commaSep1 submitButtonOption
|
||||||
|
|
||||||
return EF.SubmitButton
|
return WEF.SubmitButton
|
||||||
{ EF._submitButtonShow = maybeGetSboShow options
|
{ WEF._submitButtonShow = maybeGetSboShow options
|
||||||
}
|
}
|
||||||
|
|
||||||
data SubmitButtonOption = SboShow Bool deriving (Show, Eq)
|
data SubmitButtonOption = SboShow Bool deriving (Show, Eq)
|
||||||
@ -108,28 +109,24 @@ maybeGetSboShow options = U.headSafe [b | SboShow b <- options]
|
|||||||
|
|
||||||
-- * Fields
|
-- * Fields
|
||||||
|
|
||||||
getFieldsConfig :: [EntityFormOption] -> [EF.Field]
|
getFieldsConfig :: [EntityFormOption] -> [WEF.Field]
|
||||||
getFieldsConfig options = case [fs | EfoFields fs <- options] of
|
getFieldsConfig options = case [fs | EfoFields fs <- options] of
|
||||||
[] -> []
|
[] -> []
|
||||||
ls -> head ls
|
ls -> head ls
|
||||||
|
|
||||||
entityFormOptionFields :: Parser EntityFormOption
|
entityFormOptionFields :: Parser EntityFormOption
|
||||||
entityFormOptionFields = EfoFields <$> (P.waspPropertyClosure "fields" $ L.commaSep1 field)
|
entityFormOptionFields = EfoFields <$> PE.waspPropertyEntityFields fieldOption createFieldConfig
|
||||||
|
|
||||||
-- | Parses 'FIELD_NAME: { ... }.'
|
createFieldConfig :: (String, [FieldOption]) -> WEF.Field
|
||||||
field :: Parser EF.Field
|
createFieldConfig (fieldName, options) = WEF.Field
|
||||||
field = do
|
{ WEF._fieldName = fieldName
|
||||||
(fieldName, options) <- P.waspIdentifierClosure $ L.commaSep1 fieldOption
|
, WEF._fieldShow = maybeGetFieldOptionShow options
|
||||||
|
, WEF._fieldDefaultValue = maybeGetFieldOptionDefaultValue options
|
||||||
return EF.Field
|
|
||||||
{ EF._fieldName = fieldName
|
|
||||||
, EF._fieldShow = maybeGetFieldOptionShow options
|
|
||||||
, EF._fieldDefaultValue = maybeGetFieldOptionDefaultValue options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data FieldOption
|
data FieldOption
|
||||||
= FieldOptionShow Bool
|
= FieldOptionShow Bool
|
||||||
| FieldOptionDefaultValue EF.DefaultValue
|
| FieldOptionDefaultValue WEF.DefaultValue
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
-- | Parses a single field option, e.g. "show" or "defaultValue".
|
-- | Parses a single field option, e.g. "show" or "defaultValue".
|
||||||
@ -139,14 +136,14 @@ fieldOption = choice
|
|||||||
, FieldOptionDefaultValue <$> defaultValue
|
, FieldOptionDefaultValue <$> defaultValue
|
||||||
]
|
]
|
||||||
|
|
||||||
defaultValue :: Parser EF.DefaultValue
|
defaultValue :: Parser WEF.DefaultValue
|
||||||
defaultValue = P.waspProperty "defaultValue" $ choice
|
defaultValue = P.waspProperty "defaultValue" $ choice
|
||||||
[ EF.DefaultValueString <$> L.stringLiteral
|
[ WEF.DefaultValueString <$> L.stringLiteral
|
||||||
, EF.DefaultValueBool <$> L.bool
|
, WEF.DefaultValueBool <$> L.bool
|
||||||
]
|
]
|
||||||
|
|
||||||
maybeGetFieldOptionShow :: [FieldOption] -> Maybe Bool
|
maybeGetFieldOptionShow :: [FieldOption] -> Maybe Bool
|
||||||
maybeGetFieldOptionShow options = U.headSafe [b | FieldOptionShow b <- options]
|
maybeGetFieldOptionShow options = U.headSafe [b | FieldOptionShow b <- options]
|
||||||
|
|
||||||
maybeGetFieldOptionDefaultValue :: [FieldOption] -> Maybe EF.DefaultValue
|
maybeGetFieldOptionDefaultValue :: [FieldOption] -> Maybe WEF.DefaultValue
|
||||||
maybeGetFieldOptionDefaultValue options = U.headSafe [dv | FieldOptionDefaultValue dv <- options]
|
maybeGetFieldOptionDefaultValue options = U.headSafe [dv | FieldOptionDefaultValue dv <- options]
|
75
src/Parser/Entity/EntityList.hs
Normal file
75
src/Parser/Entity/EntityList.hs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
module Parser.Entity.EntityList
|
||||||
|
( entityList
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Text.Parsec (choice)
|
||||||
|
import Text.Parsec.String (Parser)
|
||||||
|
|
||||||
|
import qualified Wasp.EntityList as WEL
|
||||||
|
import Wasp.EntityList (EntityList)
|
||||||
|
|
||||||
|
import qualified Wasp.JsCode as WJS
|
||||||
|
|
||||||
|
import qualified Parser.JsCode
|
||||||
|
import qualified Parser.Common as P
|
||||||
|
import qualified Parser.Entity.Common as PE
|
||||||
|
import qualified Util as U
|
||||||
|
import qualified Lexer as L
|
||||||
|
|
||||||
|
-- * EntityList
|
||||||
|
|
||||||
|
-- | Parses entity list, e.g. "entity-list<Task> TaskList {...}"
|
||||||
|
entityList :: Parser EntityList
|
||||||
|
entityList = do
|
||||||
|
(entityName, listName, options) <-
|
||||||
|
P.waspElementLinkedToEntity L.reservedNameEntityList entityListOptions
|
||||||
|
|
||||||
|
return WEL.EntityList
|
||||||
|
{ WEL._name = listName
|
||||||
|
, WEL._entityName = entityName
|
||||||
|
, WEL._fields = getFieldsConfig options
|
||||||
|
}
|
||||||
|
|
||||||
|
data EntityListOption
|
||||||
|
= EloFields [WEL.Field]
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
entityListOptions :: Parser [EntityListOption]
|
||||||
|
-- TODO(matija): this could be further abstracted as waspClosureOptions option ->
|
||||||
|
-- that way we abstract L.commaSep
|
||||||
|
entityListOptions = L.commaSep entityListOption
|
||||||
|
|
||||||
|
entityListOption :: Parser EntityListOption
|
||||||
|
entityListOption = choice
|
||||||
|
[ entityListOptionFields
|
||||||
|
]
|
||||||
|
|
||||||
|
-- * Fields
|
||||||
|
|
||||||
|
getFieldsConfig :: [EntityListOption] -> [WEL.Field]
|
||||||
|
getFieldsConfig options = case [fs | EloFields fs <- options] of
|
||||||
|
[] -> []
|
||||||
|
ls -> head ls
|
||||||
|
|
||||||
|
entityListOptionFields :: Parser EntityListOption
|
||||||
|
entityListOptionFields = EloFields <$> PE.waspPropertyEntityFields fieldOption createFieldConfig
|
||||||
|
|
||||||
|
createFieldConfig :: (String, [FieldOption]) -> WEL.Field
|
||||||
|
createFieldConfig (fieldName, options) = WEL.Field
|
||||||
|
{ WEL._fieldName = fieldName
|
||||||
|
, WEL._fieldRender = maybeGetFieldOptionRender options
|
||||||
|
}
|
||||||
|
|
||||||
|
data FieldOption
|
||||||
|
= FieldOptionRender WJS.JsCode
|
||||||
|
|
||||||
|
fieldOption :: Parser FieldOption
|
||||||
|
fieldOption = choice
|
||||||
|
[ fieldOptionRender
|
||||||
|
]
|
||||||
|
|
||||||
|
fieldOptionRender :: Parser FieldOption
|
||||||
|
fieldOptionRender = FieldOptionRender <$> P.waspProperty "render" Parser.JsCode.jsCode
|
||||||
|
|
||||||
|
maybeGetFieldOptionRender :: [FieldOption] -> Maybe WJS.JsCode
|
||||||
|
maybeGetFieldOptionRender options = U.headSafe [js | FieldOptionRender js <- options]
|
@ -1,26 +0,0 @@
|
|||||||
module Parser.EntityList
|
|
||||||
( entityList
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import Text.Parsec.Char (spaces)
|
|
||||||
|
|
||||||
import qualified Wasp.EntityList as EL
|
|
||||||
import Wasp.EntityList (EntityList)
|
|
||||||
|
|
||||||
import qualified Parser.Common as P
|
|
||||||
import qualified Lexer as L
|
|
||||||
|
|
||||||
-- * EntityList
|
|
||||||
|
|
||||||
-- | Parses entity list, e.g. "entity-list<Task> TaskList {...}"
|
|
||||||
entityList :: Parser EntityList
|
|
||||||
entityList = do
|
|
||||||
(entityName, listName, _) <-
|
|
||||||
-- NOTE(matija): not supporting any options yet.
|
|
||||||
P.waspElementLinkedToEntity L.reservedNameEntityList spaces
|
|
||||||
|
|
||||||
return EL.EntityList
|
|
||||||
{ EL._name = listName
|
|
||||||
, EL._entityName = entityName
|
|
||||||
}
|
|
12
src/Parser/JsCode.hs
Normal file
12
src/Parser/JsCode.hs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module Parser.JsCode
|
||||||
|
( jsCode
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Text.Parsec.String (Parser)
|
||||||
|
import qualified Data.Text as Text
|
||||||
|
|
||||||
|
import qualified Parser.Common as P
|
||||||
|
import qualified Wasp.JsCode as WJS
|
||||||
|
|
||||||
|
jsCode :: Parser WJS.JsCode
|
||||||
|
jsCode = (WJS.JsCode . Text.pack) <$> P.waspNamedClosure "js"
|
@ -21,6 +21,12 @@ data EntityForm = EntityForm
|
|||||||
, _fields :: [Field]
|
, _fields :: [Field]
|
||||||
} deriving (Show, Eq)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
|
-- NOTE(matija): Ideally generator would not depend on this logic defined outside of it.
|
||||||
|
-- We are moving away from this approach but some parts of code (Page generator) still
|
||||||
|
-- rely on it so we cannot remove it completely yet without further refactoring.
|
||||||
|
--
|
||||||
|
-- Some record fields are note even included (e.g. _fields), we are keeping this only for the
|
||||||
|
-- backwards compatibility.
|
||||||
instance ToJSON EntityForm where
|
instance ToJSON EntityForm where
|
||||||
toJSON entityForm = object
|
toJSON entityForm = object
|
||||||
[ "name" .= _name entityForm
|
[ "name" .= _name entityForm
|
||||||
|
@ -1,16 +1,49 @@
|
|||||||
module Wasp.EntityList
|
module Wasp.EntityList
|
||||||
( EntityList(..)
|
( EntityList(..)
|
||||||
|
, Field(..)
|
||||||
|
, getConfigForField
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Aeson ((.=), object, ToJSON(..))
|
import Data.Aeson ((.=), object, ToJSON(..))
|
||||||
|
|
||||||
|
import Wasp.JsCode (JsCode)
|
||||||
|
|
||||||
|
import qualified Util as U
|
||||||
|
import qualified Wasp.Entity as Entity
|
||||||
|
|
||||||
|
|
||||||
data EntityList = EntityList
|
data EntityList = EntityList
|
||||||
{ _name :: !String -- Name of the list
|
{ _name :: !String -- Name of the list
|
||||||
, _entityName :: !String -- Name of the entity the form is linked to
|
, _entityName :: !String -- Name of the entity the form is linked to
|
||||||
|
, _fields :: [Field]
|
||||||
} deriving (Show, Eq)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
|
-- NOTE(matija): Ideally generator would not depend on this logic defined outside of it.
|
||||||
|
-- We are moving away from this approach but some parts of code (Page generator) still
|
||||||
|
-- rely on it so we cannot remove it completely yet without further refactoring.
|
||||||
|
--
|
||||||
|
-- Some record fields are note even included (e.g. _fields), we are keeping this only for the
|
||||||
|
-- backwards compatibility.
|
||||||
instance ToJSON EntityList where
|
instance ToJSON EntityList where
|
||||||
toJSON entityList = object
|
toJSON entityList = object
|
||||||
[ "name" .= _name entityList
|
[ "name" .= _name entityList
|
||||||
, "entityName" .= _entityName entityList
|
, "entityName" .= _entityName entityList
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | For a given entity field, returns its configuration from the given entity-list, if present.
|
||||||
|
-- TODO(matija): this is very similar to the same function in EntityForm, we could extract it
|
||||||
|
-- (prob. using typeclass or TH) in the future.
|
||||||
|
getConfigForField :: EntityList -> Entity.EntityField -> Maybe Field
|
||||||
|
getConfigForField entityList entityField =
|
||||||
|
U.headSafe $ filter isConfigOfInputEntityField $ _fields entityList
|
||||||
|
where
|
||||||
|
isConfigOfInputEntityField :: Field -> Bool
|
||||||
|
isConfigOfInputEntityField =
|
||||||
|
(== Entity.entityFieldName entityField) . _fieldName
|
||||||
|
|
||||||
|
-- * Field
|
||||||
|
|
||||||
|
data Field = Field
|
||||||
|
{ _fieldName :: !String
|
||||||
|
, _fieldRender :: Maybe JsCode -- Js function that renders a list field.
|
||||||
|
} deriving (Show, Eq)
|
||||||
|
14
src/Wasp/JsCode.hs
Normal file
14
src/Wasp/JsCode.hs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module Wasp.JsCode
|
||||||
|
( JsCode(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Aeson (ToJSON(..))
|
||||||
|
import Data.Text (Text)
|
||||||
|
|
||||||
|
data JsCode = JsCode !Text deriving (Show, Eq)
|
||||||
|
|
||||||
|
-- TODO(matija): Currently generator is relying on this implementation, which is not
|
||||||
|
-- ideal. Ideally all the generation logic would be in the generator. But for now this was
|
||||||
|
-- the simplest way to implement it.
|
||||||
|
instance ToJSON JsCode where
|
||||||
|
toJSON (JsCode code) = toJSON code
|
@ -3,7 +3,7 @@ module Parser.EntityFormTest where
|
|||||||
import Test.Tasty.Hspec
|
import Test.Tasty.Hspec
|
||||||
|
|
||||||
import Parser.Common (runWaspParser)
|
import Parser.Common (runWaspParser)
|
||||||
import Parser.EntityForm
|
import Parser.Entity.EntityForm
|
||||||
( entityForm
|
( entityForm
|
||||||
, submitConfig
|
, submitConfig
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import Wasp
|
|||||||
import qualified Wasp.EntityForm as EF
|
import qualified Wasp.EntityForm as EF
|
||||||
import qualified Wasp.EntityList as EL
|
import qualified Wasp.EntityList as EL
|
||||||
import qualified Wasp.Style
|
import qualified Wasp.Style
|
||||||
|
import qualified Wasp.JsCode
|
||||||
|
|
||||||
|
|
||||||
spec_parseWasp :: Spec
|
spec_parseWasp :: Spec
|
||||||
@ -88,6 +89,12 @@ spec_parseWasp =
|
|||||||
, WaspElementEntityList $ EL.EntityList
|
, WaspElementEntityList $ EL.EntityList
|
||||||
{ EL._name = "TaskList"
|
{ EL._name = "TaskList"
|
||||||
, EL._entityName = "Task"
|
, EL._entityName = "Task"
|
||||||
|
, EL._fields =
|
||||||
|
[ EL.Field
|
||||||
|
{ EL._fieldName = "description"
|
||||||
|
, EL._fieldRender = Just $ Wasp.JsCode.JsCode "task => task.description"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
`setJsImports` [ JsImport "something" [relfile|some/file|] ]
|
`setJsImports` [ JsImport "something" [relfile|some/file|] ]
|
||||||
|
@ -69,5 +69,11 @@ entity-form<Task> CreateTaskForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entity-list<Task> TaskList {
|
entity-list<Task> TaskList {
|
||||||
// Options TBD.
|
fields: {
|
||||||
|
description: {
|
||||||
|
render: {=js
|
||||||
|
task => task.description
|
||||||
|
js=}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user