Material selection

Sample that shows how to create a custom selection answer widget take from codepen that can be used as single / multi selection and that can be enabled using its answer properties to show an other option from the answer.

class MaterialSelectionExampleWidget extends NGSSelectionWidget {
    /* Sample that shows how to create a custom selection answer
        widget that can be used as single / multi selection 
        adn that can be enabled to show an other option
    */

    /*  Html template that will generate before class gets created
        All id's and css classes name will be suffixed by the uniqueId
        provided during creation of the widget */
    static get template() {
        return `<div class="selection-container">
        <input type="radio" id="selectionInput">
        <div>
            <ngs-label for="selectionInput"></ngs-label>
         </div>
        <input type="text" class="ngs-question__answer-field">
      </div>`;
    }

    /* Style that will be assigned to this widget, note that all 
      elements class names will be scoped using the widget unique id */  
    static get style() {
        return `
.selection-container {
  background-color: #d1d7dc;
  display: block;
  position: relative;
  max-width:350px;
  font-weight: 600;
  line-height: 36px;
  min-height:60px;
}

.selection-container .ngs-question__answer-field {
    width:100%;
    max-width: 100%;
    margin-top:0px;
    display:none;
}

.selection-container .ngs-question__answer-label {
  padding: 12px 30px;
  display: block;
  text-align: left;
  color: #3C454C;
  cursor: pointer;
  position: relative;
  z-index: 2;
  transition: color 200ms ease-in;
  overflow: hidden;
  max-width:350px;
  min-height:60px;
  box-sizing:border-box;
}

.selection-container .ngs-question__answer-label.ngs-question__answer-label--in-cell.ngs-question__answer-label--unset {
  padding: 0px;
}

.selection-container .ngs-question__answer-label:before {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  content: '';
  background-color: var(--ngs-accent-color);
  position: absolute;
  left: 50%;
  top: 50%;
  -webkit-transform: translate(-50%, -50%) scale3d(1, 1, 1);
          transform: translate(-50%, -50%) scale3d(1, 1, 1);
  transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
  opacity: 0;
  z-index: -1;
}

.selection-container .ngs-question__answer-label:after {
  width: 32px;
  height: 32px;
  content: '';
  border: 2px solid #D1D7DC;
  background-color: #fff;
  background-image: url("data:image/svg+xml,%3Csvg width='32' height='32' viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.414 11L4 12.414l5.414 5.414L20.828 6.414 19.414 5l-10 10z' fill='%23fff' fill-rule='nonzero'/%3E%3C/svg%3E ");
  background-repeat: no-repeat;
  background-position: 2px 3px;
  border-radius: 50%;
  z-index: 2;
  position: absolute;
  right: 30px;
  top: 50%;

  -webkit-transform: translateY(-50%);
  transform: translateY(-50%);
  cursor: pointer;
  transition: all 200ms ease-in;
}

.selection-container .ngs-question__answer-label.ngs-question__answer-label--in-cell.ngs-question__answer-label--unset:after {
    right:calc(50% - 32px / 2);
}

.selection-container input:checked ~ div  .ngs-question__answer-label {
  color: #fff;
}
.selection-container input:checked ~ div label:before {
  -webkit-transform: translate(-50%, -50%) scale3d(36, 36, 1);
          transform: translate(-50%, -50%) scale3d(36, 36, 1);
  opacity: 1;
}
.selection-container input:disabled ~ div  .ngs-question__answer-label:before {
    background:#dedede;
}

.selection-container input:checked ~ div  .ngs-question__answer-label:after {
  background-color: var(--ngs-light-primary-color);
  border-color: var(--ngs-default-primary-color);
}

.selection-container input[type="radio"] {
  width: 12px;
  height: 12px;
  order: 1;
  z-index: 2;
  position: absolute;
  right: 30px;
  top: 50%;
  -webkit-transform: translateY(-50%);
  transform: translateY(-50%);
  cursor: pointer;
  display:none;
}`;
    }

    /* Properties that can be set in from the form designer for each
     answer using this widget as an answer type in the answer properties */
    static get propertiesDef() {
        return [{
                id: 'required',
                label: 'Required',
                defaultValue: false,
                type: 'boolean'
            },
            {
                id: 'other',
                label: 'Enable other',
                defaultValue: false,
                type: 'boolean'
            }
        ];
    }

    init() {
        // Get the input element from the template
        // its important to always suffix any id 
        // of the template with the uniqueid of the widget
        this.selectionInput = this.getWidgetElementById('selectionInput');
        this.otherField = this.widgetQuerySelector('.ngs-question__answer-field');
        this.otherField.style.display = this.properties.other ? 'block' : 'none';
        this.selectionInput.checked = this.isSelected;

        // We can get property value changes at any time
        this.getProperties$(prop => {
            this.otherField.style.display = prop.other ? 'block' : 'none';
        });

        this.isDisabled$(disabled => {
            this.selectionInput.disabled = disabled;
            this.otherField.disabled = disabled;
        });

        this.isSelected$(selected => {
            this.selectionInput.checked = selected;
            if (!selected) {
                this.otherField.value = '';
            }
        });

        // Gets the current value of the wdiget
        this.getValue$(value => {
            this.otherField.value = value;
        });

        // If the user changes the text make sure to save the value
        // in the form   
        this.selectionInput.addEventListener('click', (event) => {
            this.selectionInputChecked(this.selectionInput.checked, this);
        });

        this.otherField.addEventListener('input', (event) => {
            this.otherFieldInput(this.otherField.value, this);
        });
    }

    // Post the value to the form
    selectionInputChecked(value, widget) {
        if (widget.isSelected && value) {
            widget.selectAnswer(false);
        } else {
            widget.selectAnswer(true);
        }
    }

    otherFieldInput(value, widget) {
        if (value) {
            widget.selectAnswer(value);
        } else {
            widget.selectAnswer(null);
        }
    }

    /* Will be called by the form to see if the value is valid
    If not valid must return '' (no error message shown) or the error message to show
    If its valid return null */
    isValid() {
        if (this.properties.other && this.properties.required &&
            this.selectionInput && this.selectionInput.checked &&
            !this.otherField.value) {
            if (this.otherField.classList) {
              // Add native error classes
              this.otherField.classList.add('ng-invalid');
              this.otherField.classList.add('ng-touched');
            }
            return 'FieldRequiredMessage';
        } else if (this.otherField && this.otherField.classList) {
            // Remove native error classes
            this.otherField.classList.remove('ng-invalid');
            this.otherField.classList.remove('ng-touched');
            return null;
        }
    }
}

Last updated

(c) 2024 Data Illusion Zumbrunn. All rights reserved. Reproduction strictly forbidden.