Skip to content

Instantly share code, notes, and snippets.

@bwaidelich
Last active July 17, 2019 13:09
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bwaidelich/5e17581306e19df46aa2cfe273d840bd to your computer and use it in GitHub Desktop.
Save bwaidelich/5e17581306e19df46aa2cfe273d840bd to your computer and use it in GitHub Desktop.
Atomic Fusion based Form Definition
prototype(My.Package:Form.DefaultElement) < prototype(Neos.Fusion:Component) {
# API
id = ''
name = ''
label = ''
value = null
isRequired = false
properties = Neos.Fusion:RawArray
validationErrors = Neos.Fusion:RawArray
# /API
renderer.@process.defaultElementWrapping = afx`
<div class="some-class">
<My.Package:Form.ElementLabel
id={props.id}
label={props.label}
isRequired={props.isRequired}
/>
{value}
<My.Package:Form.ElementValidationErrors
validationErrors={props.validationErrors}
/>
</div>
`
}
prototype(My.Package:Form.ElementLabel) < prototype(Neos.Fusion:Component) {
#API
id = ''
label = ''
isRequired = false
# /API
renderer.@context._classNames = ${AtomicFusion.classNames(
'some-class',
{
'some-class--required': props.isRequired
}
)}
renderer = afx`
<label class={_classNames} for={props.id} @if.hasLabel={!String.isBlank(props.label)}>
{props.label}
</label>
`
}
prototype(My.Package:Form.ElementRenderer) < prototype(Neos.Fusion:Component) {
# API
element = null
formRuntime = null
# /API
renderer = Neos.Fusion:Renderer {
type = ${props.element.type}
element {
id = ${props.element.uniqueIdentifier}
name = ${'--' + props.formRuntime.identifier + '[' + props.element.identifier + ']'}
label = ${props.element.label}
value = ${Neos.Form.elementValue(props.formRuntime, props.element)}
isRequired = ${props.element.required}
properties = ${props.element.properties}
validationErrors = ${Neos.Form.validationErrors(props.formRuntime, props.element)}
childElements = ${props.element.elements}
}
}
renderer.@process.renderCallbacks = Neos.Form.FusionRenderer:RenderCallbacks {
formRuntime = ${props.formRuntime}
formElement = ${props.element}
}
}
prototype(My.Package:Form.ElementValidationErrors) < prototype(Neos.Fusion:Component) {
#API
validationErrors = Neos.Fusion:RawArray
# /API
renderer = afx`
<Neos.Fusion:Collection collection={props.validationErrors} itemName="error" @children="itemRenderer">
<span class="some-class some-class--error">
{Translation.translate(error.code, null, error.arguments, 'ValidationErrors', 'My.Package')}
</span>
</Neos.Fusion:Collection>
`
@if.hasValidationErrors = ${!Array.isEmpty(props.validationErrors)}
}
prototype(My.Package:Form.Navigation) < prototype(Neos.Fusion:Component) {
@styleguide {
title = 'FormNavigation'
description = 'Form navigation (submit, next, previous buttons)'
props {
}
propSets {
'On first of two pages' {
numberOfPages = 2
currentPageIndex = 0
}
'On last of two pages' {
numberOfPages = 2
currentPageIndex = 1
}
'On first of three pages' {
numberOfPages = 3
currentPageIndex = 0
}
'On second of three pages' {
numberOfPages = 3
currentPageIndex = 1
}
'On last of three pages' {
numberOfPages = 3
currentPageIndex = 2
}
}
}
#API
formIdentifier = ''
currentPageIndex = 0
numberOfPages = 1
renderingOptions = Neos.Fusion:RawArray
# /API
renderer.@context.buttonName = ${'--' + props.formIdentifier + '[__currentPage]'}
renderer = afx`
<div class="some-class">
<button
type="submit"
class="some-class some-class--previous"
name={buttonName}
value={props.currentPageIndex - 1}
@if.hasPreviousPage={props.currentPageIndex > 0}
>
{props.renderingOptions.previousButtonLabel ? props.renderingOptions.previousButtonLabel : 'Zurück'}
</button>
<button
type="submit"
class="some-class some-class--next"
name={buttonName}
value={props.currentPageIndex + 1}
@if.hasNextPage={props.currentPageIndex < props.numberOfPages - 1}
>
{props.renderingOptions.nextButtonLabel ? props.renderingOptions.nextButtonLabel : 'Weiter'}
</button>
<button
type="submit"
class="some-class some-class--submit"
name={buttonName}
value={props.currentPageIndex + 1}
@if.hasNoNextPage={props.currentPageIndex == props.numberOfPages - 1}
>
{props.renderingOptions.submitButtonLabel ? props.renderingOptions.submitButtonLabel : 'Submit'}
</button>
</div>
`
}
prototype(My.Package:Form) < prototype(Neos.Fusion:Component) {
# API
formRuntime = null
# /API
renderer = afx`
<form
method="post"
id={props.formRuntime.identifier}
class="some-class"
action={request.httpRequest.uri + '#' + props.formRuntime.identifier}
enctype="multipart/form-data"
>
<div class="sone-class__content">
<h2 class="sone-class__header" @if.hasHeader={props.formRuntime.renderingOptions.header}>{props.formRuntime.renderingOptions.header}</h2>
<div style="display:none">
<Neos.Form.FusionRenderer:FormStateHiddenField />
</div>
<Neos.Fusion:Renderer
type={props.formRuntime.currentPage.type}
element.containerElement={props.formRuntime.currentPage}
element.formRuntime={props.formRuntime}
/>
</div>
<My.Package:Form.Navigation
formIdentifier={props.formRuntime.identifier}
numberOfPages={Array.length(props.formRuntime.pages)}
currentPageIndex={props.formRuntime.currentPage.index}
renderingOptions={props.formRuntime.renderingOptions.navigation ? props.formRuntime.renderingOptions.navigation : []}
/>
</form>
`
}
# HACK !?
neos_form >
neos_form = My.Package:Form {
formRuntime = ${formRuntime}
}
prototype(My.Package:Form.SomeFormConfirmation) < prototype(Neos.Fusion:Component) {
@styleguide {
title = 'Some Form Confirmation'
description = 'Confirmation page foo bar...'
props {
formValues {
title = 'Mr'
givenName = 'John'
# ...
}
}
propSets {
'Name with XXS' {
formValues {
givenName = 'Danger: <script>alert("not secure!")</script>'
}
}
}
}
# API
formValues = Neos.Fusion:RawArray
# /API
renderer = afx`
title: {String.htmlSpecialChars(props.formValues.title)}<br />
given name: {String.htmlSpecialChars(props.formValues.givenName)}<br />
`
}
prototype(My.Package:Form.SingleLineText) < prototype(My.Package:Form.DefaultElement) {
@styleguide {
title = 'SingleLineText'
description = 'Single line text Form Element (textfield)'
props {
label = 'The label'
name = 'name'
value = ''
isRequired = false
}
propSets {
'With validation error' {
validationErrors = Neos.Fusion:RawArray {
0 = Neos.Fusion:RawArray {
code = '1221560910'
}
}
}
'With two validation errors' {
validationErrors {
0 = Neos.Fusion:RawArray {
code = '1221560910'
}
1 = Neos.Fusion:RawArray {
code = '1238108068'
arguments = Neos.Fusion:RawArray {
0 = 10
}
}
}
}
}
}
renderer = afx`
<input
type="text"
id={props.id}
name={props.name}
value={props.value}
required={props.isRequired}
autocomplete={props.properties.autocomplete ? props.properties.autocomplete : false}
/>
`
}
prototype(My.Package:Form.SingleSelectDropdown) < prototype(Cornelsen.Webkatalog:Form.DefaultElement) {
@styleguide {
title = 'SingleSelectDropdown'
description = 'Single select dropdown Form Element (select)'
props {
label = 'The label'
name = 'name'
value = 'value2'
isRequired = false
properties {
prependOptionLabel = '- select -'
options = Neos.Fusion:RawArray {
value1 = 'Label 01'
value2 = 'Label 02'
value3 = 'Label 03'
}
}
}
propSets {
'With validation error' {
validationErrors = Neos.Fusion:RawArray {
0 = Neos.Fusion:RawArray {
code = '1221560910'
}
}
}
'With two validation errors' {
validationErrors {
0 = Neos.Fusion:RawArray {
code = '1221560910'
}
1 = Neos.Fusion:RawArray {
code = '1221560910'
}
}
}
}
}
renderer = afx`
<div class="some-class">
<select
id={props.id}
name={props.name}
required={props.isRequired}
autocomplete={props.properties.autocomplete ? props.properties.autocomplete : false}
>
<option value="" @if.hasPrependOptionLabel={props.properties.prependOptionLabel != null}>{props.properties.prependOptionLabel}</option>
<Neos.Fusion:Collection collection={props.properties.options} itemName="optionLabel" itemKey="optionValue" @children="itemRenderer">
<option value={optionValue} selected={props.value == optionValue}>{optionLabel}</option>
</Neos.Fusion:Collection>
</select>
</div>
`
}
prototype(My.Package:Form.SingleLineText.Definition) < prototype(Neos.Form.Builder:FormElement.Definition) {
formElementType = 'My.Package:Form.SingleLineText'
}
prototype(My.Package:Form.SingleSelectDropdown.Definition) < prototype(Neos.Form.Builder:FormElement.Definition) {
formElementType = 'My.Package:Form.SingleSelectDropdown'
properties {
prependOptionLabel = null
options = Neos.Fusion:RawArray
}
}
prototype(My.Package:Form.Finisher.EmailFinisher.Definition) < prototype(Neos.Form.Builder:Finisher.Definition) {
formElementType = 'My.Package:Form.Finisher.Email'
}
prototype(My.Package:Form.Definition) < prototype(Neos.Form.Builder:Form) {
presetName = 'somePreset'
formElementType = 'My.Package:Form'
firstPage {
formElementType = 'My.Package:Form.Page'
}
}
prototype(My.Package:Form.Finisher.FusionFinisher.Definition) < prototype(Neos.Form.Builder:Finisher.Definition) {
formElementType = 'My.Package:Form.Finisher.Fusion'
}
prototype(My.Package:Form.ContactForm) < prototype(My.Package:Form.Definition) {
identifier = 'someForm'
firstPage {
elements {
someSection = My.Package:Form.Section.Definition {
label = 'Some section label'
elements {
title = My.Package:Form.SingleSelectDropdown.Definition {
required = true
label = 'Title'
properties {
prependOptionLabel = 'Please select'
options {
Mr = 'Mr'
Mrs = 'Mrs'
}
}
}
givenName = My.Package:Form.SingleLineText.Definition {
required = true
label = 'Given name'
}
}
}
}
}
finishers {
emailFinisher = My.Package:Form.Finisher.EmailFinisher.Definition {
options {
# ...
}
}
confirmationFinisher = My.Package:Form.Finisher.FusionFinisher.Definition {
options {
fusionPrototypeName = 'My.Package:Form.SomeFormConfirmation'
}
}
}
}
Neos:
Form:
presets:
'default': ~
'fusion': ~
'somePreset':
formElementTypes:
'My.Package:Form.Mixin.Base':
renderingOptions:
skipUnknownElements: false
'My.Package:Form.Mixin.Element':
superTypes:
'My.Package:Form.Mixin.Base': true
implementationClassName: 'Neos\Form\FormElements\GenericFormElement'
'My.Package:Form':
superTypes:
'My.Package:Form.Mixin.Base': true
rendererClassName: 'Neos\Form\FusionRenderer\FusionFormRenderer'
'My.Package:Form.Page':
superTypes:
'My.Package:Form.Mixin.Base': true
implementationClassName: 'Neos\Form\Core\Model\Page'
'My.Package:Form.Section':
superTypes:
'My.Package:Form.Mixin.Base': true
implementationClassName: 'Neos\Form\FormElements\Section'
'My.Package:Form.SingleLineText':
superTypes:
'My.Package:Form.Mixin.Element': true
'My.Package:Form.SingleSelectDropdown':
superTypes:
'My.Package:Form.Mixin.Element': true
properties:
prependOptionLabel: null
options: []
finisherPresets:
'My.Package:Form.Finisher.Email':
implementationClassName: 'My\Package\Form\Finishers\EmailFinisher'
'My.Package:Form.Finisher.Fusion':
implementationClassName: 'My\Package\Form\Finishers\FusionFinisher'
options:
fusionPrototypeName: null
@bwaidelich
Copy link
Author

This requires the composer packages neos/form, neos/form-builder and neos/form-fusionrenderer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment