All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.bootstrap3.form.BootstrapFormGroupRendererDefault Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
/**
 * Copyright (C) 2014 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.helger.bootstrap3.form;

import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import com.helger.bootstrap3.BootstrapHelper;
import com.helger.bootstrap3.CBootstrapCSS;
import com.helger.bootstrap3.grid.BootstrapGridSpec;
import com.helger.commons.annotations.Nonempty;
import com.helger.commons.annotations.OverrideOnDemand;
import com.helger.commons.collections.ContainerHelper;
import com.helger.commons.string.StringHelper;
import com.helger.html.css.DefaultCSSClassProvider;
import com.helger.html.css.ICSSClassProvider;
import com.helger.html.hc.IHCControl;
import com.helger.html.hc.IHCElement;
import com.helger.html.hc.IHCElementWithChildren;
import com.helger.html.hc.IHCNode;
import com.helger.html.hc.html.AbstractHCEdit;
import com.helger.html.hc.html.HCCheckBox;
import com.helger.html.hc.html.HCDiv;
import com.helger.html.hc.html.HCLabel;
import com.helger.html.hc.html.HCRadioButton;
import com.helger.html.hc.htmlext.HCUtils;
import com.helger.validation.error.IError;
import com.helger.validation.error.IErrorList;
import com.helger.webctrls.custom.impl.HCFormLabelUtils;

/**
 * This is the default implementation of {@link IBootstrapFormGroupRenderer}
 * which performs standard rendering. It offers the possibility to modify
 * certain styling by overriding the provided protected methods.
 *
 * @author Philip Helger
 */
@Immutable
public class BootstrapFormGroupRendererDefault implements IBootstrapFormGroupRenderer
{
  public static final ICSSClassProvider CSS_CLASS_FORM_GROUP_HELP_TEXT = DefaultCSSClassProvider.create ("form-group-help-text");
  public static final ICSSClassProvider CSS_CLASS_FORM_GROUP_ERROR_TEXT = DefaultCSSClassProvider.create ("form-group-error-text");

  /**
   * Modify the first control that is inserted. This method is only called when
   * a label is present.
   *
   * @param aLabel
   *        The label that was provided. Never null.
   * @param aFirstControl
   *        The first control that was provided. Never null.
   */
  @OverrideOnDemand
  protected void modifyFirstControlIfLabelIsPresent (@Nonnull final IHCElementWithChildren  aLabel,
                                                     @Nonnull final IHCControl  aFirstControl)
  {
    // Set the default placeholder (if none is present)
    if (aFirstControl instanceof AbstractHCEdit )
    {
      final AbstractHCEdit  aEdit = (AbstractHCEdit ) aFirstControl;

      // Only check for null, so that empty string overrides this
      // default behaviour
      if (aEdit.getPlaceholder () == null)
      {
        // Trim eventually trailing ":" from string
        final String sNewPlaceholder = StringHelper.trimEnd (aLabel.getPlainText (), HCFormLabelUtils.LABEL_END);
        aEdit.setPlaceholder (sNewPlaceholder);
      }
    }
  }

  /**
   * Create the help text node
   *
   * @param aHelpText
   *        The source help text. Never null.
   * @return Never null.
   */
  @Nonnull
  @OverrideOnDemand
  protected IHCElement  createHelpTextNode (@Nonnull final IHCNode aHelpText)
  {
    final BootstrapHelpBlock aHelpBlock = new BootstrapHelpBlock ().addClass (CSS_CLASS_FORM_GROUP_HELP_TEXT);
    aHelpBlock.addChild (aHelpText);
    return aHelpBlock;
  }

  /**
   * Retrieve an optional CSS class that is provided to the final node. This
   * method is only called if a non-null and non-empty error list
   * is present.
   *
   * @param aErrorList
   *        The non-null and non-empty error list.
   * @return May be null to indicate no CSS class.
   */
  @Nullable
  @OverrideOnDemand
  protected ICSSClassProvider getResultNodeCSSClass (@Nonnull @Nonempty final IErrorList aErrorList)
  {
    if (aErrorList.containsAtLeastOneError ())
      return CBootstrapCSS.HAS_ERROR;
    if (aErrorList.containsAtLeastOneFailure ())
      return CBootstrapCSS.HAS_WARNING;
    return null;
  }

  /**
   * Create the node for a single error.
   *
   * @param aError
   *        The provided error. Never null.
   * @return Never null.
   */
  @Nonnull
  @OverrideOnDemand
  protected IHCElement  createSingleErrorNode (@Nonnull final IError aError)
  {
    final BootstrapHelpBlock aErrorBlock = new BootstrapHelpBlock ().addClass (CSS_CLASS_FORM_GROUP_ERROR_TEXT);
    aErrorBlock.addChild (aError.getErrorText ());
    return aErrorBlock;
  }

  /**
   * Callback possibility to change the finally created node before it is
   * returned. By default nothing happens in here.
   *
   * @param aForm
   *        The source form. Never null.
   * @param aFormGroup
   *        The source form group. Never null.
   * @param aFinalNode
   *        The created node so far. Never null.
   */
  @OverrideOnDemand
  protected void modifyFinalNode (@Nonnull final BootstrapForm aForm,
                                  @Nonnull final BootstrapFormGroup aFormGroup,
                                  @Nonnull final HCDiv aFinalNode)
  {}

  @Nonnull
  public IHCElement  renderFormGroup (@Nonnull final BootstrapForm aForm,
                                         @Nonnull final BootstrapFormGroup aFormGroup)
  {
    final EBootstrapFormType eFormType = aForm.getFormType ();
    final BootstrapGridSpec aLeftGrid = aForm.getLeft ();
    final BootstrapGridSpec aRightGrid = aForm.getRight ();
    final IHCElementWithChildren  aLabel = aFormGroup.getLabel ();
    final IHCNode aCtrls = aFormGroup.getCtrl ();
    final IHCNode aHelpText = aFormGroup.getHelpText ();
    final IErrorList aErrorList = aFormGroup.getErrorList ();

    final List > aAllCtrls = HCUtils.getAllHCControls (aCtrls);

    // Set CSS class to all contained controls
    BootstrapHelper.markAsFormControls (aAllCtrls);

    final IHCControl  aFirstControl = ContainerHelper.getFirstElement (aAllCtrls);
    HCDiv aFinalNode;
    final boolean bFirstControlIsCheckBox = aFirstControl instanceof HCCheckBox;
    final boolean bFirstControlIsRadioButton = aFirstControl instanceof HCRadioButton;
    if (bFirstControlIsCheckBox || bFirstControlIsRadioButton)
    {
      // Check box or radio button
      final HCDiv aCtrlDiv = new HCDiv ();
      if (bFirstControlIsCheckBox)
        aCtrlDiv.addClass (CBootstrapCSS.CHECKBOX);
      else
        if (bFirstControlIsRadioButton)
          aCtrlDiv.addClass (CBootstrapCSS.RADIO);

      if (aLabel == null || !aLabel.hasChildren ())
      {
        aCtrlDiv.addChild (new HCLabel ().addChild (aCtrls));
      }
      else
      {
        aLabel.addChild (0, aCtrls);
        aLabel.addChild (1, " ");
        aCtrlDiv.addChild (aLabel);
      }

      if (eFormType == EBootstrapFormType.HORIZONTAL)
      {
        final HCDiv aCtrlParent = new HCDiv ();
        aLeftGrid.applyOffsetTo (aCtrlParent);
        aRightGrid.applyTo (aCtrlParent);
        aFinalNode = new HCDiv ().addClass (CBootstrapCSS.FORM_GROUP).addChild (aCtrlParent.addChild (aCtrlDiv));
      }
      else
        aFinalNode = aCtrlDiv;
    }
    else
    {
      // Other control - add in form group
      aFinalNode = new HCDiv ().addClass (CBootstrapCSS.FORM_GROUP);

      if (aLabel != null && aLabel.hasChildren ())
      {
        // We have a label

        // Screen reader only....
        if (eFormType == EBootstrapFormType.INLINE)
          aLabel.addClass (CBootstrapCSS.SR_ONLY);
        else
          if (eFormType == EBootstrapFormType.HORIZONTAL)
          {
            aLabel.addClass (CBootstrapCSS.CONTROL_LABEL);
            aLeftGrid.applyTo (aLabel);
          }

        if (aFirstControl != null)
        {
          // We have a label for a control
          if (aLabel instanceof HCLabel)
            ((HCLabel) aLabel).setFor (aFirstControl);

          modifyFirstControlIfLabelIsPresent (aLabel, aFirstControl);
        }

        if (eFormType == EBootstrapFormType.HORIZONTAL)
        {
          final HCDiv aCtrlParent = new HCDiv ();
          aCtrlParent.addChild (aCtrls);
          aRightGrid.applyTo (aCtrlParent);
          aFinalNode.addChildren (aLabel, aCtrlParent);
        }
        else
          aFinalNode.addChildren (aLabel, aCtrls);
      }
      else
      {
        // No label - just add controls
        if (eFormType == EBootstrapFormType.HORIZONTAL)
        {
          final HCDiv aCtrlParent = new HCDiv ();
          aLeftGrid.applyOffsetTo (aCtrlParent);
          aRightGrid.applyTo (aCtrlParent);
          aFinalNode.addChild (aCtrlParent.addChild (aCtrls));
        }
        else
          aFinalNode.addChild (aCtrls);
      }
    }

    // Help text
    if (aHelpText != null)
    {
      final IHCElement  aHelpTextNode = createHelpTextNode (aHelpText);

      if (eFormType == EBootstrapFormType.INLINE)
        aHelpTextNode.addClass (CBootstrapCSS.SR_ONLY);

      if (eFormType == EBootstrapFormType.HORIZONTAL)
        ((HCDiv) aFinalNode.getLastChild ()).addChild (aHelpTextNode);
      else
        aFinalNode.addChild (aHelpTextNode);
    }

    // Check form errors - highlighting
    if (aErrorList != null && !aErrorList.isEmpty ())
    {
      aFinalNode.addClass (getResultNodeCSSClass (aErrorList));

      for (final IError aError : aErrorList)
      {
        final IHCElement  aErrorNode = createSingleErrorNode (aError);

        if (eFormType == EBootstrapFormType.HORIZONTAL)
        {
          aLeftGrid.applyOffsetTo (aErrorNode);
          aRightGrid.applyTo (aErrorNode);
        }
        aFinalNode.addChild (aErrorNode);
      }
    }

    modifyFinalNode (aForm, aFormGroup, aFinalNode);

    return aFinalNode;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy