ng2-simple-forms
Version:
A simple(ish) module for creating accessible, reactive forms.
1,721 lines (1,706 loc) • 208 kB
JavaScript
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