We have approximately 50 EasyForm instances that were migrated from PFG. The original PFG forms were more compact (designed using custom CSS). In some cases, fields within a field set should be displayed in a two-column layout, with two fields in a single row. The general idea is to add per-form custom CSS within the theme package, as there doesn’t seem to be a way to store individual CSS per form, unlike in PFG. Are there any better solutions to this issue?
Yes, as an example...
Bootstrap and flex.
/*
EasyForm column layouts:
we use flex to display fields and options across multiple columns
*/
.easyformForm {
fieldset {
display: flex;
flex-wrap: wrap;
background-color: #f2f2f2;
gap: 0 1em;
padding: 2rem 0 1rem 1.5rem;
legend {
font-size: 1.6rem;
}
.form-label,
.form-switch .form-check-label {
width: 100%;
font-size: 1.3rem;
margin: 0;
}
.form-check-label {
cursor: pointer;
}
.form-switch .form-check-input {
margin: .5rem 0 0 -2.25rem;
}
.form-text {
width: 100%;
font-size: 1rem;
margin: 0 0 .25rem;
}
.form-check {
width: 100%;
margin: 0 0 .25rem;
min-height: unset;
}
.field {
background-color: #fafafa;
border-radius: .5rem;
width: 100%;
display: flex;
flex-wrap: wrap;
padding: 1rem calc(var(--bs-gutter-x) * .5) 1.5rem;
margin: .75em 0;
/* grouped field-check elements in 2,3 or 4 columns */
&.group-3 {
.form-check {
width: calc(25% - (var(--bs-gutter-x) * .5));
}
}
&.group-4 {
.form-check {
width: calc(33.33% - (var(--bs-gutter-x) * .5));
}
}
&.group-6 {
.form-check {
width: calc(50% - var(--bs-gutter-x));
}
}
.container-optiongroup {
@extend .col-12;
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 0 1rem;
align-self: start;
margin: .25rem 0 0 .33rem;
width: calc(100% - .33rem);
}
&.col-3 {
width: calc(25% - var(--bs-gutter-x));
}
&.col-4 {
width: calc(33.33% - var(--bs-gutter-x));
}
&.col-6 {
width: calc(50% - var(--bs-gutter-x));
}
&.col,
&.col-12 {
width: calc(100% - var(--bs-gutter-x));
}
}
.container-fieldgroup {
@extend .col-6;
display: flex;
flex-direction: column;
width: calc(50% - calc(var(--bs-gutter-x) * .667));
.field.col-3,
.field.col-4,
.field.col-6,
.field.col,
.field.col-12 {
width: 100%;
}
.field {
&.col-3 {
.form-check {
width: calc((100% - var(--bs-gutter-x) * 3) / 4);
}
}
&.col-4 {
.form-check {
width: calc((100% - var(--bs-gutter-x) * 2) /3);
}
}
&.col-6 {
.form-check {
width: calc((100% - var(--bs-gutter-x) * 1) / 2);
}
}
&.col,
&.col-12 {
.form-check {
width: 100%;
}
}
}
}
}
.select2-container {
width: 100%;
align-self: end;
}
/*
.select2-container,
.select2-container-multi,
.select2-container-active {
@extend .form-control;
@extend .form-select;
}
*/
.select2-container,
.select2-container-multi,
.select2-container-active,
.select2-search-choice,
.select2-choices {
border: 1px solid #f2f2f2;
border-radius: .25rem;
background-image: unset;
}
}
Use col-n and group-n classes in xml model:
<field name="output_type" type="zope.schema.Choice" easyform:readOnly="False" easyform:serverSide="False" easyform:THidden="False" easyform:css_class="col-12 group-4">
<default>01_multi_pim</default>
<title>Art der Exportdaten</title>
<description>Wähle die Art der Ausgabe (Daten/Format)</description>
<values>
<element key="01_multi_pim" i18n:translate="">PIM Dateien in CSV/PDF/JPEG Format</element>
<element key="02_pdf_tmb">Technische Produktmerkblätter in PDF Format</element>
<element key="03_pdf_sdb">Sicherheitsdatenblätter in PDF Format</element>
<element key="04_pdf_farbkarte">Produkt Farbkarten in PDF Format</element>
<element key="05_img_dosen">Dosenbilder in JPEG Format</element>
<element key="06_img_farben">Farbpaspele in JPEG Format</element>
<element key="07_csv_pim">Produktdaten in CSV Format</element>
<element key="08_multi_plenty">PlentyMarkets Exports in mehrere Formate</element>
</values>
<form:widget type="z3c.form.browser.radio.RadioFieldWidget" />
</field>
<field name="output_template_pim" type="zope.schema.Choice" easyform:depends_on="condition:form.widgets.output_type~='multi_pim'; action:both;" easyform:serverSide="False" easyform:THidden="False" easyform:css_class="col-6 group-6 align-self-start">
<required>False</required>
<default>pim_zzz_magento</default>
<description>Wähle eine PIM-Export Konfigurationsvorlage aus</description>
<title>Export Schnittstelle</title>
<values>
<element key="pim_avocado">AvocadoStore</element>
<element key="pim_benz">BENZ Professional</element>
<element key="pim_bgu">BGU (Baugeräte Union)</element>
<element key="pim_ebs_rubart">Eurobaustoff (Rubart)</element>
<element key="pim_holzland">Holzland (Zentrale)</element>
<element key="pim_hornbach">Hornbach (ADAM)</element>
<element key="pim_obi">OBI (Zentrale)</element>
<element key="pim_prosol">Prosol (Relius)</element>
<element key="pim_toom">REWE / toom (Zentrale)</element>
<element key="pim_toom_vpe">REWE / toom (Artikel/VPE EANs)</element>
<element key="pim_zeg_genotop">ZEG (GenoTop)</element>
<element key="pim_zzz_magento">Web Vorlage - Magento</element>
<element key="pim_zzz_manomano">Web Vorlage - ManoMano</element>
</values>
<form:widget type="z3c.form.browser.radio.RadioFieldWidget" />
</field>
Diazo rules:
<!--
#########################################################################
Inline XSL directives must be placed directly inside the root <rules> tag
#########################################################################
-->
<!-- EasyForm:
Field Template:
Matches div elements with the class field.
Groups elements with the fieldgroup class and wraps them in a container-fieldgroup div.
Non-grouped fields are copied as-is.
Grouped Fieldgroup Template:
Copies fieldgroup elements within the fieldgroup mode.
Applies templates to attributes and child nodes.
Form-Check Template:
Matches div elements with the class form-check that are in grouped fields.
Groups consecutive form-check elements and wraps them in a container-optiongroup div.
Prevents duplication by ensuring the otherwise clause does not copy the form-check elements.
Grouped Form-Check Template:
Copies form-check elements within the form-check mode.
Applies templates to attributes and child nodes.
-->
<!-- Template to match .field elements that are members of a .fieldgroup and wrap them in a .container-fieldgroup div -->
<xsl:template match="div[contains(@class, 'field')]">
<xsl:choose>
<xsl:when test="contains(@class, 'fieldgroup')">
<xsl:if test="not(preceding-sibling::div[contains(@class, 'fieldgroup')])">
<div class="container-fieldgroup">
<xsl:apply-templates select="." mode="fieldgroup"/>
<xsl:apply-templates select="following-sibling::div[contains(@class, 'fieldgroup')]" mode="fieldgroup"/>
</div>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Template to copy grouped .fieldgroup elements -->
<xsl:template match="div[contains(@class, 'fieldgroup')]" mode="fieldgroup">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- Template to match a group of .form-check elements and wrap them in a .container-optiongroup div -->
<xsl:template match="div//div[contains(@class, 'form-check')]">
<xsl:choose>
<xsl:when test="not(preceding-sibling::div[contains(@class, 'form-check')])">
<div class="container-optiongroup">
<xsl:apply-templates select="." mode="form-check"/>
<xsl:apply-templates select="following-sibling::div[contains(@class, 'form-check')]" mode="form-check"/>
</div>
</xsl:when>
<xsl:otherwise>
<!-- Prevent duplication by omitting the otherwise part, we are grouping all .form-check elements inside .fieldgroup elements. -->
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Template to copy grouped .form-check elements -->
<xsl:template match="div[contains(@class, 'form-check')]" mode="form-check">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
Our customers use the "custom css" field in the advanced tab. there you can add bootstrap grid classes to add some column styles.
just tried it on the demo page ... not sure how long this will be online Ein Formular — Deutsch
Our solution looks like this:
- through a behaviour, we inject a layout option that let the user choose a layout option
- internally, we inject the layout information like
easyform-layout-1
as CSS class into the HTML - on the theme layer, we implement the various layout options using SCSS and CSS grid
This solves most of our use cases with the 50 forms. In some cases, we need some hard-coded CSS for some particular fieldsets. But even in such cases, we can provide a standard CSS if the id of a particular fieldset follows a certain naming.
This approach allows us to hand over the layout decisions to the user rather than having some custom CSS on the theme level per form.
This is a 1:1 migration from PFG...everything else is left to the customer.