UNPKG

ng2-simple-forms

Version:

A simple(ish) module for creating accessible, reactive forms.

1,721 lines (1,706 loc) 208 kB
import { Subject } from 'rxjs/Subject'; import { Component, EventEmitter, Input, Output, Directive, ElementRef, Renderer2, ViewChild, NgModule } from '@angular/core'; import { FormArray, FormBuilder, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { toWords } from 'number-to-words'; import { CommonModule } from '@angular/common'; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class FormElement { /** * @param {?} data */ constructor(data) { this.setArrayValues = (data) => { this.formArrayValue.next(data); }; this.setConfig = (property, value) => { this.config[property] = value; return this; }; this.getConfig = (property) => { return this.config[property]; }; this.setProperty = (property, value) => { this[property] = value; return this; }; this.setAccessibility = (property, value) => { this.accessibilityConfig[property] = value; return this; }; this.setStyle = (property, value) => { this.stylesConfig[property] = value; return this; }; this.getStyle = (property) => { return this.stylesConfig[property]; }; this.getAccessibility = (property) => { return this.accessibilityConfig[property]; }; this.getTestId = (optionValue) => { return optionValue ? `${this.inputId}_${optionValue}_${this.type}` : `${this.inputId}_${this.type}`; }; this.inputId = data.inputId; this.type = data.type; this.subType = data.subType; this.label = data.label; this.required = data.required; this.minLength = data.minLength; this.maxLength = data.maxLength; this.regex = data.regex; this.helpText = data.helpText; this.errorText = data.errorText; this.options = data.options; this.optionGroups = data.optionGroups; this.optionObjects = data.optionObjects; this.config = data.config || {}; this.accessibilityConfig = data.accessibilityConfig || {}; this.stylesConfig = data.stylesConfig || {}; this.formArrayControls = data.formArrayControls; this.formArrayValue = data.formArrayValue || new Subject(); this.formArraySingularName = data.formArraySingularName; } } class ComponentValue { /** * @param {?} data */ constructor(data) { this.inputId = data.inputId; this.value = data.value; this.isValid = data.isValid; } } class ElementOption { /** * @param {?} data */ constructor(data) { this.value = data.value; this.display = data.display; } } class ElementOptionGroup { /** * @param {?} data */ constructor(data) { this.groupName = data.groupName; this.options = data.options; } } class ObjectGroup { /** * @param {?} data */ constructor(data) { this.groupName = data.groupName; this.objects = data.objects; } } /** * FormDetails object for unwrapped forms * \@TODO - refer to this as a loose form? Does that make more sense? Who knows! 🙈 */ class FormDetails { /** * @param {?=} data */ constructor(data) { // Get an element by its inputId this.get = (inputId) => { let /** @type {?} */ foundElement; this.elements.forEach((element) => { if (element.inputId === inputId) { foundElement = element.element; } }); return foundElement; }; // Set config on a particular element by its inputId this.setConfig = (inputId, propertyName, value) => { const /** @type {?} */ foundElement = this.get(inputId); foundElement.setConfig(propertyName, value); this.elements.forEach((element) => { if (element.inputId === foundElement.inputId) { element.element = foundElement; } }); return this; }; // Set style on a particular element by its inputId this.setStyle = (inputId, propertyName, value) => { const /** @type {?} */ foundElement = this.get(inputId); foundElement.setStyle(propertyName, value); this.elements.forEach((element) => { if (element.inputId === foundElement.inputId) { element.element = foundElement; } }); return this; }; // Set style for all elements in the formDetails this.setGlobalStyle = (propertyName, value) => { this.elements.forEach((element) => { const /** @type {?} */ elementTmp = element.element; elementTmp.setStyle(propertyName, value); element.element = elementTmp; }); return this; }; // Set accessibility on a particular element by its inputId this.setAccessibility = (inputId, propertyName, value) => { const /** @type {?} */ foundElement = this.get(inputId); foundElement.setAccessibility(propertyName, value); this.elements.forEach((element) => { if (element.inputId === foundElement.inputId) { element.element = foundElement; } }); return this; }; this.elements = data ? data.elements : []; this.formGroup = data ? data.formGroup : undefined; } } /** * @record */ class Elements { } Elements.Text = 'text'; Elements.Select = 'select'; Elements.Checkbox = 'checkbox'; Elements.Radio = 'radio'; Elements.Password = 'password'; Elements.Datepicker = 'date'; Elements.Range = 'range'; Elements.Textarea = 'textarea'; Elements.ObjectSelector = 'object'; Elements.FormArray = 'formArray'; class Properties { } Properties.InputId = 'inputId'; Properties.Required = 'required'; Properties.MinLength = 'minLength'; Properties.MaxLength = 'maxLength'; Properties.Regex = 'regex'; Properties.HelpText = 'helpText'; Properties.ErrorText = 'errorText'; Properties.Options = 'options'; Properties.OptionGroups = 'optionGroups'; /** * @record */ class Config { } Config.RequiredMarker = 'requiredMarker'; Config.ReadOnly = 'readOnly'; Config.ObjectDisplayProperty = 'objectDisplayProperty'; Config.ObjectGroupProperty = 'objectGroupProperty'; Config.ShouldGroupObjects = 'shouldGroupObjects'; /** * @record */ class Accessibility { } Accessibility.AriaLabel = 'ariaLabel'; Accessibility.AriaDescribedBy = 'ariaDescribedBy'; Accessibility.AriaLabelledBy = 'ariaLabelledBy'; Accessibility.AriaReadOnly = 'ariaReadOnly'; /** * @record */ class Styles { } Styles.ElementWrapper = 'wrapperCssClass'; Styles.GroupLabel = 'groupLabelCssClass'; Styles.ElementLabel = 'elementLabelCssClass'; Styles.ElementInput = 'elementInputCssClass'; Styles.Fieldset = 'fieldsetCssClass'; Styles.Legend = 'legendCssClass'; Styles.OptionLabel = 'optionLabelCssClass'; /** * @record */ /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class FormComponent { constructor() { this.hideClear = false; this.formOptions = { wrapperCssClass: '', // Allows you to pass a custom CSS class to apply to the form wrapper container formElementCssClass: '', // Allows you to pass a custom CSS class to apply to each form element in the form buttonCssClass: '', // Allows you to pass a customer CSS class to apply to each button in the form submitButtonLabel: 'Submit', // Allows you to modify the submit button text clearButtonLabel: 'Clear' // Allows you to modify the clear button text }; this.changeEmitter = new EventEmitter(); this.submitEmitter = new EventEmitter(); } /** * @return {?} */ ngOnInit() { this.form.formGroup.valueChanges.subscribe(data => { this.changeEmitter.emit(data); }); this.form.elements = this.form.elements.map((element) => { return { inputId: element.inputId, element: this.setElementConfig(element.element) }; }); if (this.defaultFormData) { this.form.formGroup.setValue(this.defaultFormData); } } /** * @param {?} element * @return {?} */ setElementConfig(element) { element.setConfig('wrapperCssClass', this.formOptions.formElementCssClass); element.setConfig('formElementCssClass', this.formOptions.formElementCssClass); return element; } /** * @return {?} */ submit() { if (this.form.formGroup.valid) { this.submitEmitter.emit(this.form.formGroup.getRawValue()); return; } } /** * @return {?} */ clear() { this.form.formGroup.reset(); } /** * @return {?} */ isValid() { return this.form.formGroup.valid; } } FormComponent.decorators = [ { type: Component, args: [{ selector: 'app-form', template: `<app-form-wrapper [wrapperCssClass]="formOptions.wrapperCssClass"> <h1>{{ formTitle }}</h1> <h2>{{ formSubtitle }}</h2> <app-form-element *ngFor="let element of form.elements" [formGroup]="form.formGroup" [formElement]="element.element"> </app-form-element> <span class="controls"> <button class="{{ formOptions.buttonCssClass || 'btn btn-primary' }}" [ngClass]="{ 'disabled' : !isValid() }" (click)="submit()"> {{ formOptions.submitButtonLabel || 'Submit' }} </button> <button *ngIf="!hideClear" class="{{ formOptions.buttonCssClass || 'btn btn-primary' }}" (click)="clear()">{{ formOptions.clearButtonLabel || 'Clear' }}</button> </span> </app-form-wrapper> `, styles: [`h2{ font-size:12px; color:#5b6b8c; margin-top:-20px; } .controls{ margin-top:15px; display:block; } .disabled{ background-color:lightgray; color:black; cursor:not-allowed; } `] },] }, ]; /** @nocollapse */ FormComponent.ctorParameters = () => []; FormComponent.propDecorators = { "formTitle": [{ type: Input },], "formSubtitle": [{ type: Input },], "form": [{ type: Input },], "hideClear": [{ type: Input },], "formOptions": [{ type: Input },], "defaultFormData": [{ type: Input },], "formData": [{ type: Output },], "changeEmitter": [{ type: Output },], "submitEmitter": [{ type: Output },], }; /** * @record */ /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class ErrorMessageComponent { constructor() { } /** * @return {?} */ ngOnInit() { } } ErrorMessageComponent.decorators = [ { type: Component, args: [{ selector: 'app-error-message', template: `<span role="alert" [attr.aria-expanded]="true" class="error-text">{{ message }}</span> `, styles: [`.error-text{ display:block; position:absolute; background-color:#e3272e; font-size:12px; color:white; padding:5px 10px; text-align:center; border-radius:5px; z-index:2; bottom:-32px; left:25%; min-width:50%; min-height:40px; } .error-text::after{ content:" "; position:absolute; bottom:100%; left:50%; margin-left:-5px; border-width:5px; border-style:solid; border-color:transparent transparent #e3272e transparent; } `] },] }, ]; /** @nocollapse */ ErrorMessageComponent.ctorParameters = () => []; ErrorMessageComponent.propDecorators = { "message": [{ type: Input },], }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class FormElementComponent { constructor() { } /** * @return {?} */ ngOnInit() { } } FormElementComponent.decorators = [ { type: Component, args: [{ selector: 'app-form-element', template: `<div *ngIf="formElement" [ngSwitch]="formElement.type.toLowerCase()"> <!-- TEXT INPUT FIELD --> <app-text-input *ngSwitchCase="'text'" [formGroup]="formGroup" [elementData]="formElement" ></app-text-input> <!-- TEXT AREA FIELD --> <app-text-area *ngSwitchCase="'textarea'" [formGroup]="formGroup" [elementData]="formElement" ></app-text-area> <!-- DATE PICKER FIELD --> <app-date-picker *ngSwitchCase="'date'" [formGroup]="formGroup" [elementData]="formElement"> </app-date-picker> <!-- PASSWORD INPUT FIELD --> <app-password-input *ngSwitchCase="'password'" [formGroup]="formGroup" [elementData]="formElement"> </app-password-input> <!-- SELECT INPUT FIELD --> <app-dropdown-question *ngSwitchCase="'select'" [formGroup]="formGroup" [elementData]="formElement"> </app-dropdown-question> <!-- CHECKBOX GROUP FIELD --> <app-checkbox *ngSwitchCase="'checkbox'" [formGroup]="formGroup" [elementData]="formElement"> </app-checkbox> <!-- RADIO GROUP FIELD --> <app-radio-question *ngSwitchCase="'radio'" [formGroup]="formGroup" [elementData]="formElement"> </app-radio-question> <!-- RANGE FIELD --> <app-range *ngSwitchCase="'range'" [formGroup]="formGroup" [elementData]="formElement"> </app-range> <!-- OBJECT SELECTOR --> <app-object-selector *ngSwitchCase="'object'" [formGroup]="formGroup" [elementData]="formElement"> </app-object-selector> <!-- FORM ARRAY --> <app-form-array-element *ngSwitchCase="'formarray'" [formValue]="formElement.formArrayValue" [formGroup]="formGroup" [elementData]="formElement"> </app-form-array-element> </div> `, styles: [``] },] }, ]; /** @nocollapse */ FormElementComponent.ctorParameters = () => []; FormElementComponent.propDecorators = { "formGroup": [{ type: Input },], "formElement": [{ type: Input },], }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class FormWrapperComponent { constructor() { } /** * @return {?} */ ngOnInit() { } } FormWrapperComponent.decorators = [ { type: Component, args: [{ selector: 'app-form-wrapper', template: `<div class="{{wrapperCssClass || 'default-theme'}}"> <ng-content></ng-content> </div> `, styles: [`.form-wrapper{ background-color:white; padding:30px; border-radius:8px; margin-bottom:30px; } `] },] }, ]; /** @nocollapse */ FormWrapperComponent.ctorParameters = () => []; FormWrapperComponent.propDecorators = { "wrapperCssClass": [{ type: Input },], }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class SimpleFormBuilder { constructor() { } /** * Get Validators from FormElement definition properties * @param {?} element * @return {?} */ static getValidators(element) { const /** @type {?} */ validators = []; if (element.required) { validators.push(Validators.required); } if (element.minLength) { const /** @type {?} */ minLength = typeof element.minLength === 'number' ? element.minLength : parseInt(element.minLength, 10); validators.push(Validators.minLength(minLength)); } if (element.maxLength) { const /** @type {?} */ maxLength = typeof element.maxLength === 'number' ? element.maxLength : parseInt(element.maxLength, 10); validators.push(Validators.maxLength(maxLength)); } if (element.regex) { validators.push(Validators.pattern(element.regex)); } return validators; } /** * Create a FormGroup from a list FormElement definitions * @param {?} formElements * @return {?} */ static toFormGroup(formElements) { const /** @type {?} */ formGroup = {}; formElements.forEach((element) => { if (element.type === Elements.FormArray) { const /** @type {?} */ arrayControls = element.formArrayControls; const /** @type {?} */ arrayGroup = {}; arrayControls.forEach((arrayElement) => { arrayGroup[arrayElement.inputId] = ['', SimpleFormBuilder.getValidators(arrayElement)]; }); formGroup[element.inputId] = new FormArray([SimpleFormBuilder.formBuilder.group(arrayGroup)]); } else { formGroup[element.inputId] = ['', SimpleFormBuilder.getValidators(element)]; } }); return SimpleFormBuilder.formBuilder.group(formGroup); } /** * Create a 'FormDetails' * Takes a group of FormElements, and returns an * Unwrapped form object of formGroup, and * { inputId: string, element: FormElement } array. * @param {?} formElements * @return {?} */ static toFormDetails(formElements) { const /** @type {?} */ unwrappedForm = new FormDetails(); unwrappedForm.elements = formElements.map(item => ({ inputId: item.inputId, element: item })); unwrappedForm.formGroup = SimpleFormBuilder.toFormGroup(formElements); return unwrappedForm; } /** * Create a form from JSON * @param {?} jsonValue * @return {?} */ static fromJson(jsonValue) { const /** @type {?} */ elements = jsonValue.map(value => { return new FormElement(value); }); return SimpleFormBuilder.toFormDetails(elements); } /** * Simple wrapper to create a FormElement with basic * attributes * @param {?} type * @param {?} label * @param {?=} options * @param {?=} config * @return {?} */ static createElement(type, label, options = {}, config) { let /** @type {?} */ elementInputId; if (options && !options.inputId) { elementInputId = SimpleFormBuilder.toInputId(label); } else { elementInputId = options.inputId; } const /** @type {?} */ element = new FormElement({ inputId: elementInputId, type: type, label: label, config: config }); return SimpleFormBuilder.setOptions(element, options); } /** * @param {?} element * @param {?} options * @return {?} */ static setOptions(element, options) { Object.getOwnPropertyNames(options).forEach(optionKey => { element.setProperty(optionKey, options[optionKey]); }); return element; } /** * @param {?} str * @return {?} */ static toInputId(str) { const /** @type {?} */ words = str.split(' '); const /** @type {?} */ mutated = words.map(function (word, index) { // If it is the first word make sure to lowercase all the chars. if (index === 0) { return word.toLowerCase(); } // If it is not the first word only upper case the first char and lowercase the rest. return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); }); return mutated.join(''); } } SimpleFormBuilder.formBuilder = new FormBuilder(); /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class ElementBaseComponent { constructor() { this.changeEmitter = new EventEmitter(); this.isFocussed = false; } /** * @return {?} */ ngOnInit() { } /** * @return {?} */ initElement() { this.getFormGroup(); this.setElementStyles(); } /** * @return {?} */ getId() { const /** @type {?} */ id = this.elementData.getConfig('arrayIndex') ? `${this.elementData.inputId}_${this.elementData.getConfig('arrayIndex')}` : `${this.elementData.inputId}`; return this.elementData.inputId; } /** * @return {?} */ setElementStyles() { this.elementWrapperClass = this.elementData.getStyle(Styles.ElementWrapper); this.elementGroupLabelClass = this.elementData.getStyle(Styles.GroupLabel); this.elementLabelClass = this.elementData.getStyle(Styles.ElementLabel); this.elementInputClass = this.elementData.getStyle(Styles.ElementInput); this.elementFieldsetClass = this.elementData.getStyle(Styles.Fieldset); this.elementLegendClass = this.elementData.getStyle(Styles.Legend); this.elementOptionLabelClass = this.elementData.getStyle(Styles.OptionLabel); } /** * Emit a ComponentValue when this element value changes * (Used when an element is rendered as single element without a FormGroup) * @return {?} */ emit() { this.formGroup.valueChanges.subscribe(value => { this.changeEmitter.emit(new ComponentValue({ inputId: this.elementData.inputId, value: value[this.elementData.inputId], isValid: this.formGroup.valid })); }); } /** * Get the relevent data about this element * (Used for accessibility directive) * @param {?=} option * @return {?} */ getElementData(option) { return { elementData: this.elementData, formGroup: this.formGroup, option: option }; } /** * Get this elements LabelConfig as an object to pass to LabelComponent * @return {?} */ getLabelConfig() { return /** @type {?} */ ({ isFocussed: this.isFocussed, elementData: this.elementData, inputHasValue: this.hasValue(), requiredMarker: this.getRequiredMarker(), inputIsInvalid: this.invalid(), inputIsValid: this.valid(), testId: this.elementData.getTestId() }); } /** * Get the default FormGroup for this element * (If part of a form, use forms Formgroup, else, create a FormGroup for JUST this element) * @return {?} */ getFormGroup() { this.formGroup = this.formGroup ? this.formGroup : this.toOwnFormgroup(); this.elementData.setProperty('formGroup', this.formGroup); this.emit(); } /** * Create a single element FormGroup from this element * @return {?} */ toOwnFormgroup() { return SimpleFormBuilder.toFormGroup([this.elementData]); } /** * Has the current element got focus? * (Used to apply 'has-focus' class to surrounding DOM elements (label, etc) * @return {?} */ hasFocus() { return this.isFocussed; } /** * Check if element has config options * @return {?} */ hasConfig() { return !!(this.elementData && this.elementData.config); } /** * Get a custom marker for required fields. If none supplied * in config, return * * @return {?} */ getRequiredMarker() { if (this.hasConfig()) { return this.elementData.config.requiredMarker || '*'; } } /** * Toggle focus of element input, and activate validation listener * @param {?} hasFocus * @return {?} */ toggleFocus(hasFocus) { this.isFocussed = hasFocus; this.activateValidationListener(); } /** * Subscribe to the elements value changes to activate validation * on change, rather than on lose focus * @return {?} */ activateValidationListener() { this.getElement().valueChanges.subscribe((value) => { if (value && !this.touched()) { this.formGroup.get(this.elementData.inputId).markAsTouched(); } }); } /** * Simple accessor for getting the FormControl by inputId * @return {?} */ getElement() { return /** @type {?} */ (this.formGroup.get(this.elementData.inputId)); } /** * Convert string to camelCase inputId * \@TODO - Remove from here and move to utils * @param {?} str * @return {?} */ toInputId(str) { const /** @type {?} */ words = str.split(' '); const /** @type {?} */ mutated = words.map(function (word, index) { // If it is the first word make sure to lowercase all the chars. if (index === 0) { return word.toLowerCase(); } // If it is not the first word only upper case the first char and lowercase the rest. return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); }); return mutated.join(''); } /** * @return {?} */ valid() { return this.formGroup.controls[this.elementData.inputId].touched && this.formGroup.controls[this.elementData.inputId].valid; } /** * @return {?} */ invalid() { return this.formGroup.controls[this.elementData.inputId].touched && this.formGroup.controls[this.elementData.inputId].invalid; } /** * @return {?} */ touched() { return this.formGroup.controls[this.elementData.inputId].touched; } /** * @return {?} */ hasValue() { if (this.formGroup && this.elementData) { return !!this.formGroup.get(this.elementData.inputId).value; } return false; } } ElementBaseComponent.decorators = [ { type: Component, args: [{ selector: 'app-element-base', template: ``, styles: [``] },] }, ]; /** @nocollapse */ ElementBaseComponent.ctorParameters = () => []; ElementBaseComponent.propDecorators = { "formGroup": [{ type: Input },], "elementData": [{ type: Input },], "changeEmitter": [{ type: Output },], }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class CheckboxComponent extends ElementBaseComponent { constructor() { super(); } /** * @return {?} */ ngOnInit() { this.getFormGroup(); this.setElementStyles(); } /** * @param {?} value * @return {?} */ updateParentFormGroup(value) { this.formGroup.controls[this.elementData.inputId].setValue(value); this.formGroup.controls[this.elementData.inputId].markAsTouched(); if (!this.isValid()) { this.formGroup.get(this.elementData.inputId).setErrors({ invalid: 'Incorrect number of options selected' }); } } /** * @return {?} */ isValid() { const /** @type {?} */ minChoices = this.elementData.minLength || 0; const /** @type {?} */ maxChoices = this.elementData.maxLength || this.getDefaultTotalOptions(); const /** @type {?} */ value = this.formGroup.get(this.elementData.inputId).value; let /** @type {?} */ valueCount = 0; Object.getOwnPropertyNames(value).forEach(key => { if (value[key]) { valueCount++; } }); const /** @type {?} */ isValid = (valueCount >= minChoices && valueCount <= maxChoices) && this.formGroup.controls[this.elementData.inputId].touched; return isValid; } /** * @return {?} */ showCheckboxError() { return !!this.elementData.errorText && !this.isValid() && this.isTouched(); } /** * @return {?} */ isTouched() { return this.formGroup.get(this.elementData.inputId).touched; } /** * @return {?} */ getDefaultTotalOptions() { if (this.elementData.options) { return this.elementData.options.length; } else { let /** @type {?} */ count = 0; this.elementData.optionGroups.forEach((optionGroup) => { count += optionGroup.options.length; }); return count; } } } CheckboxComponent.decorators = [ { type: Component, args: [{ selector: 'app-checkbox', template: `<app-grouped-checkbox-question *ngIf="elementData.optionGroups" [formGroup]="formGroup" [elementData]="elementData"> </app-grouped-checkbox-question> <app-ungrouped-checkbox-question *ngIf="!elementData.optionGroups" [formGroup]="formGroup" [elementData]="elementData"> </app-ungrouped-checkbox-question> `, styles: [``] },] }, ]; /** @nocollapse */ CheckboxComponent.ctorParameters = () => []; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class DatePickerComponent extends ElementBaseComponent { constructor() { super(); } /** * @return {?} */ ngOnInit() { this.initElement(); } } DatePickerComponent.decorators = [ { type: Component, args: [{ selector: 'app-date-picker', template: `<div class="field form-group {{elementWrapperClass}}" [formGroup]="formGroup"> <!-- INPUT LABEL --> <app-label [labelConfig]="getLabelConfig()"> </app-label> <!-- INPUT FIELD --> <input [appDefaultAccessibility]="getElementData()" [attr.aria-invalid]="!valid()" id="{{getId()}}" formControlName="{{elementData.inputId}}" type="date" (focus)="toggleFocus(true)" (blur)="toggleFocus(false)" class="form-control {{elementInputClass}}" [ngClass]='{ valid: valid(), invalid: invalid() }'/> <!-- VALIDATION MESSAGES --> <app-validation-messages [elementData]="elementData" [hasError]="invalid()" [hasFocus]="hasFocus()"></app-validation-messages> </div> `, styles: [`fieldset{ padding:0; margin:0; border:0; min-width:0; } legend{ display:block; width:100%; padding:0; margin-bottom:20px; font-size:21px; line-height:inherit; color:#333333; border:0; border-bottom:1px solid #e5e5e5; } label{ display:inline-block; max-width:100%; margin-bottom:5px; font-weight:bold; } input[type="search"]{ -webkit-box-sizing:border-box; box-sizing:border-box; } input[type="radio"], input[type="checkbox"]{ margin:4px 0 0; margin-top:1px \\9; line-height:normal; } input[type="file"]{ display:block; } input[type="range"]{ display:block; width:100%; } select[multiple], select[size]{ height:auto; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus{ outline:5px auto -webkit-focus-ring-color; outline-offset:-2px; } output{ display:block; padding-top:7px; font-size:14px; line-height:1.42857; color:#555555; } .form-control{ display:block; width:100%; height:34px; padding:6px 12px; font-size:14px; line-height:1.42857; color:#555555; background-color:#fff; background-image:none; border:1px solid #ccc; border-radius:4px; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; -webkit-transition:border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; } .form-control:focus{ border-color:#66afe9; outline:0; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); } .form-control::-moz-placeholder{ color:#999; opacity:1; } .form-control:-ms-input-placeholder{ color:#999; } .form-control::-webkit-input-placeholder{ color:#999; } .form-control::-ms-expand{ border:0; background-color:transparent; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control{ background-color:#eeeeee; opacity:1; } .form-control[disabled], fieldset[disabled] .form-control{ cursor:not-allowed; } textarea.form-control{ height:auto; } input[type="search"]{ -webkit-appearance:none; } @media screen and (-webkit-min-device-pixel-ratio: 0){ input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control{ line-height:34px; } input[type="date"].input-sm, .input-group-sm input[type="date"], input[type="time"].input-sm, .input-group-sm input[type="time"], input[type="datetime-local"].input-sm, .input-group-sm input[type="datetime-local"], input[type="month"].input-sm, .input-group-sm input[type="month"]{ line-height:30px; } input[type="date"].input-lg, .input-group-lg input[type="date"], input[type="time"].input-lg, .input-group-lg input[type="time"], input[type="datetime-local"].input-lg, .input-group-lg input[type="datetime-local"], input[type="month"].input-lg, .input-group-lg input[type="month"]{ line-height:46px; } } .form-group{ margin-bottom:15px; } .radio, .checkbox{ position:relative; display:block; margin-top:10px; margin-bottom:10px; } .radio label, .checkbox label{ min-height:20px; padding-left:20px; margin-bottom:0; font-weight:normal; cursor:pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"]{ position:absolute; margin-left:-20px; margin-top:4px \\9; } .radio + .radio, .checkbox + .checkbox{ margin-top:-5px; } .radio-inline, .checkbox-inline{ position:relative; display:inline-block; padding-left:20px; margin-bottom:0; vertical-align:middle; font-weight:normal; cursor:pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline{ margin-top:0; margin-left:10px; } input[type="radio"][disabled], input[type="radio"].disabled, fieldset[disabled] input[type="radio"], input[type="checkbox"][disabled], input[type="checkbox"].disabled, fieldset[disabled] input[type="checkbox"]{ cursor:not-allowed; } .radio-inline.disabled, fieldset[disabled] .radio-inline, .checkbox-inline.disabled, fieldset[disabled] .checkbox-inline{ cursor:not-allowed; } .radio.disabled label, fieldset[disabled] .radio label, .checkbox.disabled label, fieldset[disabled] .checkbox label{ cursor:not-allowed; } .form-control-static{ padding-top:7px; padding-bottom:7px; margin-bottom:0; min-height:34px; } .form-control-static.input-lg, .form-control-static.input-sm{ padding-left:0; padding-right:0; } .input-sm{ height:30px; padding:5px 10px; font-size:12px; line-height:1.5; border-radius:3px; } select.input-sm{ height:30px; line-height:30px; } textarea.input-sm, select[multiple].input-sm{ height:auto; } .form-group-sm .form-control{ height:30px; padding:5px 10px; font-size:12px; line-height:1.5; border-radius:3px; } .form-group-sm select.form-control{ height:30px; line-height:30px; } .form-group-sm textarea.form-control, .form-group-sm select[multiple].form-control{ height:auto; } .form-group-sm .form-control-static{ height:30px; min-height:32px; padding:6px 10px; font-size:12px; line-height:1.5; } .input-lg{ height:46px; padding:10px 16px; font-size:18px; line-height:1.33333; border-radius:6px; } select.input-lg{ height:46px; line-height:46px; } textarea.input-lg, select[multiple].input-lg{ height:auto; } .form-group-lg .form-control{ height:46px; padding:10px 16px; font-size:18px; line-height:1.33333; border-radius:6px; } .form-group-lg select.form-control{ height:46px; line-height:46px; } .form-group-lg textarea.form-control, .form-group-lg select[multiple].form-control{ height:auto; } .form-group-lg .form-control-static{ height:46px; min-height:38px; padding:11px 16px; font-size:18px; line-height:1.33333; } .has-feedback{ position:relative; } .has-feedback .form-control{ padding-right:42.5px; } .form-control-feedback{ position:absolute; top:0; right:0; z-index:2; display:block; width:34px; height:34px; line-height:34px; text-align:center; pointer-events:none; } .input-lg + .form-control-feedback, .input-group-lg + .form-control-feedback, .form-group-lg .form-control + .form-control-feedback{ width:46px; height:46px; line-height:46px; } .input-sm + .form-control-feedback, .input-group-sm + .form-control-feedback, .form-group-sm .form-control + .form-control-feedback{ width:30px; height:30px; line-height:30px; } .has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline, .has-success.radio label, .has-success.checkbox label, .has-success.radio-inline label, .has-success.checkbox-inline label{ color:#3c763d; } .has-success .form-control{ border-color:#3c763d; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-success .form-control:focus{ border-color:#2b542c; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; } .has-success .input-group-addon{ color:#3c763d; border-color:#3c763d; background-color:#dff0d8; } .has-success .form-control-feedback{ color:#3c763d; } .has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline, .has-warning.radio label, .has-warning.checkbox label, .has-warning.radio-inline label, .has-warning.checkbox-inline label{ color:#8a6d3b; } .has-warning .form-control{ border-color:#8a6d3b; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-warning .form-control:focus{ border-color:#66512c; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; } .has-warning .input-group-addon{ color:#8a6d3b; border-color:#8a6d3b; background-color:#fcf8e3; } .has-warning .form-control-feedback{ color:#8a6d3b; } .has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline, .has-error.radio label, .has-error.checkbox label, .has-error.radio-inline label, .has-error.checkbox-inline label{ color:#a94442; } .has-error .form-control{ border-color:#a94442; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-error .form-control:focus{ border-color:#843534; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; } .has-error .input-group-addon{ color:#a94442; border-color:#a94442; background-color:#f2dede; } .has-error .form-control-feedback{ color:#a94442; } .has-feedback label ~ .form-control-feedback{ top:25px; } .has-feedback label.sr-only ~ .form-control-feedback{ top:0; } .help-block{ display:block; margin-top:5px; margin-bottom:10px; color:#737373; } @media (min-width: 768px){ .form-inline .form-group{ display:inline-block; margin-bottom:0; vertical-align:middle; } .form-inline .form-control{ display:inline-block; width:auto; vertical-align:middle; } .form-inline .form-control-static{ display:inline-block; } .form-inline .input-group{ display:inline-table; vertical-align:middle; } .form-inline .input-group .input-group-addon, .form-inline .input-group .input-group-btn, .form-inline .input-group .form-control{ width:auto; } .form-inline .input-group > .form-control{ width:100%; } .form-inline .control-label{ margin-bottom:0; vertical-align:middle; } .form-inline .radio, .form-inline .checkbox{ display:inline-block; margin-top:0; margin-bottom:0; vertical-align:middle; } .form-inline .radio label, .form-inline .checkbox label{ padding-left:0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"]{ position:relative; margin-left:0; } .form-inline .has-feedback .form-control-feedback{ top:0; } } .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline{ margin-top:0; margin-bottom:0; padding-top:7px; } .form-horizontal .radio, .form-horizontal .checkbox{ min-height:27px; } .form-horizontal .form-group{ margin-left:-15px; margin-right:-15px; } .form-horizontal .form-group:before, .form-horizontal .form-group:after{ content:" "; display:table; } .form-horizontal .form-group:after{ clear:both; } @media (min-width: 768px){ .form-horizontal .control-label{ text-align:right; margin-bottom:0; padding-top:7px; } } .form-horizontal .has-feedback .form-control-feedback{ right:15px; } @media (min-width: 768px){ .form-horizontal .form-group-lg .control-label{ padding-top:11px; font-size:18px; } } @media (min-width: 768px){ .form-horizontal .form-group-sm .control-label{ padding-top:6px; font-size:12px; } } fieldset{ display:block; width:100%; padding:6px 12px; font-size:14px; line-height:1.42857143; color:#555555; background-color:#fff; background-image:none; border:1px solid #ccc; border-radius:4px; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; -webkit-transition:border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; } .field{ position:relative; } .form-group{ display:block; } .form-control.valid{ border-color:#7bb752; } .form-control.valid:focus{ border-color:#28a745; -webkit-box-shadow:0 0 0 0.2rem rgba(40, 167, 69, 0.25); box-shadow:0 0 0 0.2rem rgba(40, 167, 69, 0.25); } .form-control.invalid{ border-color:red; } .form-control.invalid:focus{ border-color:#dc3545; -webkit-box-shadow:0 0 0 0.2rem rgba(220, 53, 69, 0.25); box-shadow:0 0 0 0.2rem rgba(220, 53, 69, 0.25); } `, ``] },] }, ]; /** @nocollapse */ DatePickerComponent.ctorParameters = () => []; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class DropdownQuestionComponent extends ElementBaseComponent { constructor() { super(); } /** * @return {?} */ ngOnInit() { this.initElement(); } } DropdownQuestionComponent.decorators = [ { type: Component, args: [{ selector: 'app-dropdown-question', template: `<app-grouped-dropdown-question [formGroup]="formGroup" *ngIf="elementData.optionGroups" [elementData]="elementData"></app-grouped-dropdown-question> <app-ungrouped-dropdown-question [formGroup]="formGroup" *ngIf="!elementData.optionGroups" [elementData]="elementData"></app-ungrouped-dropdown-question> `, styles: [`select{ width:100%; padding:15px; margin-bottom:3px; } .field{ position:relative; } `] },] }, ]; /** @nocollapse */ DropdownQuestionComponent.ctorParameters = () => []; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class PasswordInputComponent extends ElementBaseComponent { constructor() { super(); } /** * @return {?} */ ngOnInit() { this.initElement(); } } PasswordInputComponent.decorators = [ { type: Component, args: [{ selector: 'app-password-input', template: `<div class="form-group {{elementWrapperClass}}" [formGroup]="formGroup"> <!-- INPUT LABEL --> <app-label [labelConfig]="getLabelConfig()"> </app-label> <!-- INPUT FIELD --> <input [appDefaultAccessibility]="getElementData()" [attr.aria-invalid]="!valid()" id="{{getId()}}" formControlName="{{elementData.inputId}}" (focus)="toggleFocus(true)" (blur)="toggleFocus(false)" type="password" class="form-control {{elementInputClass}}" [ngClass]='{ valid: valid(), invalid: invalid() }'/> <!-- VALIDATION MESSAGES --> <app-validation-messages [elementData]="elementData" [hasError]="invalid()" [hasFocus]="hasFocus()"></app-validation-messages> </div> `, styles: [`fieldset{ padding:0; margin:0; border:0; min-width:0; } legend{ display:block; width:100%; padding:0; margin-bottom:20px; font-size:21px; line-height:inherit; color:#333333; border:0; border-bottom:1px solid #e5e5e5; } label{ display:inline-block; max-width:100%; margin-bottom:5px; font-weight:bold; } input[type="search"]{ -webkit-box-sizing:border-box; box-sizing:border-box; } input[type="radio"], input[type="checkbox"]{ margin:4px 0 0; margin-top:1px \\9; line-height:normal; } input[type="file"]{ display:block; } input[type="range"]{ display:block; width:100%; } select[multiple], select[size]{ height:auto; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus{ outline:5px auto -webkit-focus-ring-color; outline-offset:-2px; } output{ display:block; padding-top:7px; font-size:14px; line-height:1.42857; color:#555555; } .form-control{ display:block; width:100%; height:34px; padding:6px 12px; font-size:14px; line-height:1.42857; color:#555555; background-color:#fff; background-image:none; border:1px solid #ccc; border-radius:4px; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; -webkit-transition:border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; transition:border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s, -webkit-box-shadow ease-in-out 0.15s; } .form-control:focus{ border-color:#66afe9; outline:0; -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); } .form-control::-moz-placeholder{ color:#999; opacity:1; } .form-control:-ms-input-placeholder{ color:#999; } .form-control::-webkit-input-placeholder{ color:#999; } .form-control::-ms-expand{ border:0; background-color:transparent; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control{ background-color:#eeeeee; opacity:1; } .form-control[disabled], fieldset[disabled] .form-control{ cursor:not-allowed; } textarea.form-control{ height:auto; } input[type="search"]{ -webkit-appearance:none; } @media screen and (-webkit-min-device-pixel-ratio: 0){ input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control{ line-height:34px; } input[type="date"].input-sm, .input-group-sm input[type="date"], input[type="time"].input-sm, .input-group-sm input[type="time"], input[type="datetime-local"].input-sm, .input-group-sm input[type="datetime-local"], input[type="month"].input-sm, .input-group-sm input[type="month"]{ line-height:30px; } input[type="date"].input-lg, .input-group-lg input[type="date"], input[type="time"].input-lg, .input-group-lg input[type="time"], input[type="datetime-local"].input-lg, .input-group-lg input[type="datetime-local"], input[type="month"].input-lg, .input-group-lg input[type="month"]{ line-height:46px; } } .form-group{ margin-bottom:15px; } .radio, .checkbox{ position:relative; display:block; margin-top:10px; margin-bottom:10px; } .radio label, .checkbox label{ min-height:20px; padding-left:20px; margin-bottom:0; font-weight:normal; cursor:pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"]{ position:absolute; margin-left:-20px; margin-top:4px \\9; } .radio + .radio, .checkbox + .checkbox{ margin-top:-5px; } .radio-inline, .checkbox-inline{ position:relative; display:inline-block; padding-left:20px; margin-bottom:0; vertical-align:middle; font-weight:normal; cursor:pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline{ margin-top:0; margin-left:10px; } input[type="radio"][disabled], input[type="radio"].disabled, fieldset[disabled] input[type="radio"], input[type="checkbox"][disabled], input[type="checkbox"].disabled, fieldset[disabled] input[type="checkbox"]{ cursor:not-allowed; } .radio-inline.disabled, fieldset[disabled] .radio-inline, .checkbox-inline.disabled, fieldset[disabled] .checkbox-inline{ cursor:not-allowed; } .radio.disabled label, fieldset[disabled] .radio label, .checkbox.disabl