@skireal/form-builder
Version:
Form builder for Angular apps
1,047 lines (1,038 loc) • 157 kB
JavaScript
import { __decorate } from 'tslib';
import { EventEmitter, Input, Output, Component, ViewEncapsulation, ɵɵdefineInjectable, ɵɵinject, Injectable, ApplicationRef, ComponentFactoryResolver, Injector, INJECTOR, Pipe, NgModule } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
import { BehaviorSubject } from 'rxjs';
import { CommonModule } from '@angular/common';
let FormBuilderComponent = class FormBuilderComponent {
constructor() {
this.enableGeneralFields = true;
this.enableConditionalLogicBlocks = false;
this.isSurvey = true;
this.incomingFormData = '';
this.enableSetValidationOptions = false;
this.locale = {};
this.jsonCreated = new EventEmitter();
this.formData = {
formData: {
steps: [],
generalFields: []
},
options: {
name: '',
type: '',
country: ''
},
uniqueFormData: []
};
}
ngOnInit() {
this.formData = JSON.parse(this.incomingFormData);
}
onSaveClicked($event) {
const jsonData = JSON.stringify($event);
this.jsonCreated.emit(jsonData);
}
};
__decorate([
Input()
], FormBuilderComponent.prototype, "enableGeneralFields", void 0);
__decorate([
Input()
], FormBuilderComponent.prototype, "enableConditionalLogicBlocks", void 0);
__decorate([
Input()
], FormBuilderComponent.prototype, "isSurvey", void 0);
__decorate([
Input()
], FormBuilderComponent.prototype, "incomingFormData", void 0);
__decorate([
Input()
], FormBuilderComponent.prototype, "enableSetValidationOptions", void 0);
__decorate([
Input()
], FormBuilderComponent.prototype, "locale", void 0);
__decorate([
Output()
], FormBuilderComponent.prototype, "jsonCreated", void 0);
FormBuilderComponent = __decorate([
Component({
selector: 'form-builder',
template: "<div>\n <app-ui\n (saveClicked)=\"onSaveClicked($event)\"\n [enableGeneralFields]=\"enableGeneralFields\"\n [enableConditionalLogicBlocks]=\"enableConditionalLogicBlocks\"\n [isSurvey]=\"isSurvey\"\n [incomingFormData]=\"formData\"\n [enableSetValidationOptions]=\"enableSetValidationOptions\"\n [locale]=\"locale\">\n </app-ui>\n</div>\n",
encapsulation: ViewEncapsulation.None,
styles: ["@font-face{font-display:block;font-family:bootstrap-icons;src:url(https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6) format(\"woff2\"),url(https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6) format(\"woff\")}:root{--fb-body-bg:#fff;--fb-body-color:#212529;--fb-border-radius:0.375rem;--fb-border-radius-lg:0.5rem;--fb-border-width:1px;--fb-border-color:#dee2e6;--fb-body-font-family:var(--fb-font-sans-serif);--fb-body-font-weight:400;--fb-body-line-height:1.5;--fb-font-sans-serif:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",\"Noto Sans\",\"Liberation Sans\",Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\";--fb-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace}input.ng-invalid.ng-touched{border:1px solid red;box-shadow:0 0 0 .1rem rgba(255,76,76,.966)}.cdk-drag-preview{z-index:10000005!important}.form-builder__form-control{display:block;width:100%;height:auto;box-sizing:border-box;padding:6px 12px;font-size:16px;font-weight:400;line-height:1.5;text-align:start;color:#212529;background-color:#fff;border:.8px solid #dee2e6;border-radius:6px;box-shadow:none;cursor:text;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-builder__form-control-sm{padding:4px 8px;font-size:13px;border-radius:3px}.form-builder__form-select{display:block;align-items:center;box-sizing:border-box;width:100%;padding:6px 12px;overflow:visible;font-size:16px;font-weight:400;line-height:1.5;text-align:start;color:#212529;background-color:#fff;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\");background-position:calc(100% - 12px) 50%;background-repeat:no-repeat;background-size:16px 12px;border:.8px solid #dee2e6;border-radius:6px;box-shadow:none;cursor:default;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none;pointer-events:auto}.form-builder__form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .05rem rgba(13,110,253,.25)}.text-right{text-align:right!important}.text-left{text-align:left!important}.text-center{text-align:center!important}.form-builder__row .form-builder__col{flex-shrink:0;width:100%;max-width:100%;padding-right:12px;padding-left:12px}.form-builder__row{display:flex;flex-wrap:wrap;margin-left:-12px;margin-right:-12px}.form-builder__col{flex:1 0 0%}.p-0{padding:0!important}@media (min-width:1200px){.form-builder__col-xl-2{flex:0 0 auto;width:16.66666667%}}"]
})
], FormBuilderComponent);
var FormFieldType;
(function (FormFieldType) {
FormFieldType["Text"] = "text";
FormFieldType["Textarea"] = "textarea";
FormFieldType["Date"] = "date";
FormFieldType["Select"] = "select";
FormFieldType["Number"] = "number";
FormFieldType["Checkbox"] = "checkbox";
FormFieldType["CheckboxGroup"] = "checkbox-group";
FormFieldType["Radio"] = "radio";
FormFieldType["NeedContact"] = "need-contact";
FormFieldType["File"] = "file";
FormFieldType["Password"] = "password";
FormFieldType["Email"] = "email";
FormFieldType["Phone"] = "phone";
FormFieldType["Likert"] = "likert";
FormFieldType["Csat"] = "csat";
FormFieldType["CES"] = "ces";
FormFieldType["NPS"] = "nps";
FormFieldType["QE"] = "qe";
FormFieldType["ContactName"] = "contact-name";
FormFieldType["ContactSurname"] = "contact-surname";
FormFieldType["ContactEmail"] = "contact-email";
FormFieldType["ContactPhone"] = "contact-phone";
FormFieldType["CountryDropdown"] = "country-dropdown";
})(FormFieldType || (FormFieldType = {}));
var ValidatorType;
(function (ValidatorType) {
ValidatorType["Required"] = "required";
ValidatorType["MinLength"] = "minlength";
ValidatorType["MaxLength"] = "maxlength";
ValidatorType["Pattern"] = "pattern";
ValidatorType["Email"] = "email";
ValidatorType["Min"] = "min";
ValidatorType["Max"] = "max";
ValidatorType["RequiredTrue"] = "requiredTrue";
})(ValidatorType || (ValidatorType = {}));
const formFieldTypes = [
FormFieldType.Text,
FormFieldType.Textarea,
FormFieldType.Date,
FormFieldType.Select,
FormFieldType.Number,
FormFieldType.Checkbox,
FormFieldType.CheckboxGroup,
FormFieldType.Radio,
FormFieldType.NeedContact,
FormFieldType.File,
FormFieldType.Password,
FormFieldType.Email,
FormFieldType.Phone,
FormFieldType.Likert,
FormFieldType.Csat,
FormFieldType.CES,
FormFieldType.NPS,
FormFieldType.QE
];
const surveyFieldTypes = [
FormFieldType.CountryDropdown,
FormFieldType.Text,
FormFieldType.Textarea,
FormFieldType.Date,
FormFieldType.Select,
FormFieldType.Checkbox,
FormFieldType.CheckboxGroup,
FormFieldType.Radio,
FormFieldType.Likert,
FormFieldType.Csat,
FormFieldType.CES,
FormFieldType.NPS,
FormFieldType.QE,
FormFieldType.NeedContact,
FormFieldType.ContactName,
FormFieldType.ContactSurname,
FormFieldType.ContactEmail,
FormFieldType.ContactPhone
];
const fieldTypesNames = {
[FormFieldType.CountryDropdown]: 'Country',
[FormFieldType.Text]: 'Text Input',
[FormFieldType.Textarea]: 'Text Area',
[FormFieldType.Date]: 'Date',
[FormFieldType.Select]: 'Select',
[FormFieldType.Number]: 'Number',
[FormFieldType.Checkbox]: 'Checkbox',
[FormFieldType.CheckboxGroup]: 'Checkboxes',
[FormFieldType.Radio]: 'Radio',
[FormFieldType.File]: 'File Attachment',
[FormFieldType.Password]: 'Password',
[FormFieldType.Email]: 'Email',
[FormFieldType.Phone]: 'Phone Number',
[FormFieldType.Likert]: 'Likert Scale',
[FormFieldType.Csat]: 'CSAT Scale',
[FormFieldType.CES]: 'CES Scale',
[FormFieldType.NPS]: 'NPS Scale',
[FormFieldType.QE]: 'QE Group',
[FormFieldType.NeedContact]: 'Need Contact',
[FormFieldType.ContactName]: 'Contact Name',
[FormFieldType.ContactSurname]: 'Contact Surname',
[FormFieldType.ContactEmail]: 'Contact Email',
[FormFieldType.ContactPhone]: 'Contact Phone'
};
const uniqueFieldTypes = [
FormFieldType.NeedContact,
FormFieldType.ContactName,
FormFieldType.ContactSurname,
FormFieldType.ContactEmail,
FormFieldType.ContactPhone
];
const validatorTypes = [
ValidatorType.Required,
ValidatorType.MinLength,
ValidatorType.MaxLength,
ValidatorType.Pattern,
ValidatorType.Email,
ValidatorType.Min,
ValidatorType.Max,
ValidatorType.RequiredTrue
];
const commonFields = [
{ id: 'active', name: 'Active', isArray: false },
{ id: 'classes', name: 'Classes', isArray: false, placeholder: 'e.g., class1 class2' },
{ id: 'placeholder', name: 'Placeholder', isArray: false },
{ id: 'title', name: 'Title', isArray: false },
{ id: 'description', name: 'Description', isArray: false },
{ id: 'validators', name: 'Validators', isArray: true },
{ id: 'required', name: 'Required', isArray: false },
{ id: 'warningMessage', name: 'Warning message', isArray: false },
{ id: 'analyticsTitle', name: 'Analytics title', isArray: false },
{ id: 'step', name: 'Move to step', isArray: false }
];
const fieldsByType = {
[FormFieldType.Text]: [
...commonFields,
{ id: 'feedBackText', name: 'Use this field when creating feedback text', isArray: false }
],
[FormFieldType.Textarea]: [
...commonFields,
{ id: 'feedBackText', name: 'Use this field when creating feedback text', isArray: false }
],
[FormFieldType.Date]: [...commonFields],
[FormFieldType.Select]: [
...commonFields,
{ id: 'options', name: 'Options', isArray: true },
{ id: 'hasOther', name: 'Has other', isArray: false }
],
[FormFieldType.Number]: [...commonFields],
[FormFieldType.Checkbox]: [...commonFields],
[FormFieldType.CheckboxGroup]: [
...commonFields,
{ id: 'options', name: 'Options', isArray: true },
{ id: 'hasOther', name: 'Has other', isArray: false }
],
[FormFieldType.Radio]: [
...commonFields,
{ id: 'options', name: 'Options', isArray: true },
{ id: 'hasOther', name: 'Has other', isArray: false }
],
[FormFieldType.NeedContact]: [
...commonFields,
{ id: 'options', name: 'Options', isArray: true },
{ id: 'defaultValue', name: 'Default value', isArray: false }
],
[FormFieldType.File]: [...commonFields],
[FormFieldType.Password]: [...commonFields],
[FormFieldType.Email]: [...commonFields],
[FormFieldType.Phone]: [...commonFields],
[FormFieldType.Likert]: [
...commonFields,
{ id: 'optionsTitle', name: 'Options title', isArray: false },
{ id: 'options', name: 'Options', isArray: true },
{ id: 'rows', name: 'Rows', isArray: true }
],
[FormFieldType.Csat]: [
...commonFields,
{ id: 'firstAnswer', name: 'First answer', isArray: false },
{ id: 'lastAnswer', name: 'Last answer', isArray: false },
{ id: 'hasNA', name: 'Has N/A', isArray: false }
],
[FormFieldType.CES]: [...commonFields, { id: 'hasNA', name: 'Has N/A', isArray: false }],
[FormFieldType.NPS]: [
...commonFields,
{
id: 'comment',
name: 'Comment',
isArray: false,
isObject: true,
objectFields: [
{ id: 'commentTitle', name: 'Comment Title', isArray: false },
{ id: 'commentSubtitle', name: 'Comment Subtitle', isArray: false },
{ id: 'commentWarningMessage', name: 'Comment Warning Message', isArray: false }
]
},
{ id: 'firstAnswer', name: 'First answer', isArray: false },
{ id: 'lastAnswer', name: 'Last answer', isArray: false }
],
[FormFieldType.QE]: [
...commonFields,
{ id: 'firstAnswer', name: 'First answer', isArray: false },
{ id: 'lastAnswer', name: 'Last answer', isArray: false },
{
id: 'qeScales',
name: 'QE scales',
isArray: true,
children: [
{
id: 'qeScaleChildren',
name: 'QE Scale Children',
isArray: true,
parentArray: 'qeScales'
}
]
}
],
[FormFieldType.ContactName]: [...commonFields],
[FormFieldType.ContactSurname]: [...commonFields],
[FormFieldType.ContactEmail]: [...commonFields],
[FormFieldType.ContactPhone]: [...commonFields],
[FormFieldType.CountryDropdown]: [...commonFields]
};
const defaultOptionValues = [];
const controlsMap = {
validators: ['type', 'value', 'errormsg'],
options: ['name', 'value', 'country'],
rows: ['title'],
qeScales: ['title', 'subtitle', 'qeScaleChildren'],
qeScaleChildren: ['title']
};
const defaultConditionalLogicBlock = {
selectedField: {
type: '',
id: '',
name: ''
},
selectedCondition: '',
conditionValue: '',
selectedAction: '',
selectedTargetField: '',
type: 'conditionalLogicBlock'
};
const conditionOptions = {
text: ['equals', 'contains', 'is empty', 'is not empty'],
checkbox: ['is empty', 'is not empty'],
number: [
'equals',
'not equals',
'greater',
'less',
'greater or equals',
'less or equals',
'is empty',
'is not empty'
]
};
const haveOptionsFieldTypes = [
'select',
'checkbox-group',
'radio',
'likert',
'need-contact'
];
const withoutValueValidatorTypes = ['required', 'requiredTrue', 'email'];
const booleanFields = ['active', 'required', 'hasOther', 'hasNA'];
const arrayProperties = ['validators', 'options', 'rows', 'qeScales'];
const defaultValuesMap = {
options: {
country: 'ZZ'
}
};
const maxLengthMap = {
placeholder: 250,
title: 1000,
warningMessage: 1000,
analyticsTitle: 250
};
const requiredSubfieldsMap = {
options: ['name', 'value'],
rows: ['title'],
qeScales: ['title']
};
let UiFormService = class UiFormService {
constructor(fb) {
this.fb = fb;
this.fieldsToCreate = [];
this.fieldsToCreateSubject = new BehaviorSubject(this.fieldsToCreate);
}
createFormGroup(addedFields) {
const formGroupConfig = this.generateFormGroupConfig(addedFields);
return this.fb.group(formGroupConfig);
}
generateFormGroupConfig(addedFields) {
const formGroupConfig = {};
for (const field of addedFields) {
formGroupConfig[field.id] = this.createControlForField(field);
}
return formGroupConfig;
}
createControlForField(field) {
if (field.isArray) {
return this.createFormArray();
}
else if (field.isObject && field.objectFields) {
return this.createFormGroupForObject(field.objectFields);
}
else {
return this.createControl(field.defaultValue);
}
}
createFormGroupForObject(objectFields) {
const formGroupConfig = {};
for (const field of objectFields) {
formGroupConfig[field.id] = this.createControlForField(field);
}
return this.fb.group(formGroupConfig);
}
createControl(defaultValue = '') {
return this.fb.control(defaultValue);
}
createFormArray() {
return this.fb.array([]);
}
addControlToFormArray(formArray, arrayName, nestedArrayConfig) {
const newGroup = this.createGroupForArray(arrayName, nestedArrayConfig);
formArray.push(newGroup);
}
removeControlFromFormArray(formArray, index) {
formArray.removeAt(index);
}
createGroupForArray(arrayName, nestedArrayConfig) {
const group = this.fb.group({});
const controlFields = controlsMap[arrayName] || [];
if (controlFields) {
controlFields.forEach((fieldName) => {
var _a;
if (nestedArrayConfig && fieldName in nestedArrayConfig) {
group.addControl(fieldName, nestedArrayConfig[fieldName]);
}
else {
const defaultValue = ((_a = defaultValuesMap[arrayName]) === null || _a === void 0 ? void 0 : _a[fieldName]) || '';
group.addControl(fieldName, this.createControl(defaultValue));
}
});
}
else {
console.log(`Invalid controlName: ${arrayName}`);
}
return group;
}
saveFieldProperties(form, fieldType) {
var _a, _b;
const fieldOptions = Object.assign(Object.assign({}, form.value), { validators: (_a = form.value.validators) === null || _a === void 0 ? void 0 : _a.filter((validator) => Object.values(validator).some((property) => property !== '')), type: fieldType, id: this.generateUniqueId().toString(), options: (_b = form.value.options) === null || _b === void 0 ? void 0 : _b.map((option) => (Object.assign(Object.assign({}, option), { id: this.generateUniqueId().toString() }))) });
booleanFields.forEach((field) => {
if (typeof fieldOptions[field] !== 'boolean') {
fieldOptions[field] = false;
}
});
if (fieldType === 'nps' && form.value.comment) {
const isCommentEmpty = Object.values(fieldOptions.comment || {}).every((value) => value === '' || value === null || value === undefined);
if (!isCommentEmpty) {
fieldOptions.comment = Object.assign(Object.assign({}, fieldOptions.comment), { commentId: this.generateUniqueId().toString() });
}
}
if (fieldType === 'qe' && form.value.qeScales) {
fieldOptions.qeScales = form.value.qeScales.map((scale) => {
var _a;
return (Object.assign(Object.assign({}, scale), { id: this.generateUniqueId().toString(), qeScaleChildren: (_a = scale.qeScaleChildren) === null || _a === void 0 ? void 0 : _a.map((child) => (Object.assign(Object.assign({}, child), { id: this.generateUniqueId().toString() }))) }));
});
}
if (fieldType === 'likert' && form.value.rows) {
fieldOptions.rows = form.value.rows.map((row) => (Object.assign(Object.assign({}, row), { id: this.generateUniqueId().toString() })));
}
return fieldOptions;
}
generateUniqueId() {
return Math.floor(Math.random() * 100000000).toString();
}
setFieldsToCreate(fieldType) {
this.fieldsToCreate = fieldsByType[fieldType];
this.fieldsToCreateSubject.next(this.fieldsToCreate);
}
getFieldsToCreate() {
return this.fieldsToCreateSubject.asObservable();
}
getRequiredFields(fieldType) {
const baseFields = ['title', 'analyticsTitle'];
const typeSpecificFields = {
select: ['options'],
'checkbox-group': ['options'],
radio: ['options'],
likert: ['options', 'rows'],
qe: ['qeScales']
};
return [...baseFields, ...(typeSpecificFields[fieldType] || [])];
}
};
UiFormService.ctorParameters = () => [
{ type: FormBuilder }
];
UiFormService.ɵprov = ɵɵdefineInjectable({ factory: function UiFormService_Factory() { return new UiFormService(ɵɵinject(FormBuilder)); }, token: UiFormService, providedIn: "root" });
UiFormService = __decorate([
Injectable({
providedIn: 'root'
})
], UiFormService);
const defaultFormOptionsObject = {
formData: {
steps: [
{
addedFields: [
{
id: 'id',
name: 'name',
title: 'title'
}
],
conditionalLogicBlocks: []
}
],
generalFields: []
},
options: {
name: 'formName',
type: 'formType',
country: 'NG'
},
uniqueFormData: []
};
let FormDataService = class FormDataService {
constructor() {
this.formOptionsFull = new BehaviorSubject(defaultFormOptionsObject);
}
setFormData(data) {
this.formOptionsFull.next(data);
}
getFormData() {
return this.formOptionsFull.asObservable();
}
prepareFormData(formData) {
const formOptionsFullObject = formData;
const formDataSteps = formData.formData.steps;
formDataSteps.forEach((stepFormData, index) => {
const stepData = [];
stepFormData.addedFields.forEach((field) => {
var _a, _b, _c;
const fieldData = Object.assign({}, field);
if ('step' in fieldData) {
delete fieldData.step;
}
if (fieldData.type === 'need-contact') {
delete fieldData.options;
}
const hasNonRequiredValidator = (_a = field.validators) === null || _a === void 0 ? void 0 : _a.some((validator) => validator.type !== 'required');
const hasRequiredValidator = (_b = field.validators) === null || _b === void 0 ? void 0 : _b.some((validator) => validator.type === 'required');
if (hasNonRequiredValidator && !hasRequiredValidator) {
(_c = fieldData.validators) === null || _c === void 0 ? void 0 : _c.push({
type: 'required',
errormsg: 'This field is required'
});
}
stepData.push(fieldData);
});
formOptionsFullObject.formData.steps[index] = {
title: `Step ${index + 1}`,
addedFields: stepData,
conditionalLogicBlocks: stepFormData.conditionalLogicBlocks
};
});
return formOptionsFullObject;
}
};
FormDataService.ɵprov = ɵɵdefineInjectable({ factory: function FormDataService_Factory() { return new FormDataService(); }, token: FormDataService, providedIn: "root" });
FormDataService = __decorate([
Injectable({
providedIn: 'root'
})
], FormDataService);
let ConfirmationDialogComponent = class ConfirmationDialogComponent {
constructor() {
this.message = 'Are you sure?';
this.confirm = new EventEmitter();
}
ngOnInit() { }
confirmAction() {
this.confirm.emit(true);
}
cancelAction() {
this.confirm.emit(false);
}
};
__decorate([
Input()
], ConfirmationDialogComponent.prototype, "message", void 0);
__decorate([
Output()
], ConfirmationDialogComponent.prototype, "confirm", void 0);
ConfirmationDialogComponent = __decorate([
Component({
selector: 'app-confirmation-dialog',
template: "<div class=\"form-builder__confirmation-dialog\">\n <div class=\"form-builder__confirmation-dialog__content\">\n <p>{{ message }}</p>\n <div class=\"form-builder__confirmation-dialog__actions\">\n <button (click)=\"confirmAction()\" class=\"form-builder__btn form-builder__btn-primary\">\n Yes\n </button>\n <button (click)=\"cancelAction()\" class=\"form-builder__btn form-builder__btn-secondary\">\n No\n </button>\n </div>\n </div>\n</div>\n",
styles: [".form-builder__btn{display:inline-block;box-sizing:border-box;align-items:flex-start;padding:6px 12px;font-weight:400;text-align:center;-webkit-text-decoration:none #fff solid auto;text-decoration:none #fff solid auto;text-indent:0;text-shadow:none;text-transform:none;vertical-align:middle;word-spacing:normal;letter-spacing:normal;line-height:24px;color:#fff;background-color:rgba(169,157,184,.8);border:.8px solid rgba(169,157,184,.8);border-radius:6px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:auto;text-rendering:auto;-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}.form-builder__btn-sm{padding:3px 10px;font-size:.75rem}.form-builder__btn-primary{background-color:rgba(169,157,184,.8);border-color:rgba(169,157,184,.8)}.form-builder__btn-primary:active,.form-builder__btn-primary:hover{background-color:#a99db8;border-color:#a99db8}.form-builder__btn-secondary{background-color:rgba(148,169,160,.8);border-color:rgba(148,169,160,.8)}.form-builder__btn-secondary:active,.form-builder__btn-secondary:hover{background-color:#94a9a0;border-color:#94a9a0}.form-builder__btn-danger{background-color:rgba(230,88,88,.8);border-color:rgba(230,88,88,.8)}.form-builder__btn-danger:active,.form-builder__btn-danger:hover{background-color:#e65858;border-color:#e65858}.form-builder__btn-success{background-color:rgba(81,167,76,.8);border-color:rgba(81,167,76,.8)}.form-builder__btn-success:active,.form-builder__btn-success:hover{background-color:#51a74c;border-color:#51a74c}.form-builder__confirmation-dialog{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);display:flex;justify-content:center;align-items:center;z-index:505}.form-builder__confirmation-dialog__actions{margin-bottom:5px;margin-top:5px;text-align:center}.form-builder__confirmation-dialog__actions .form-builder__btn{margin-right:10px}.form-builder__confirmation-dialog__actions .form-builder__btn:last-child{margin-right:0}.form-builder__confirmation-dialog__content{background:#fff;padding:20px;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,.2);text-align:center}"]
})
], ConfirmationDialogComponent);
let ConfirmationService = class ConfirmationService {
constructor(appRef, componentFactoryResolver, injector) {
this.appRef = appRef;
this.componentFactoryResolver = componentFactoryResolver;
this.injector = injector;
this.dialogComponentRef = null;
}
open(message) {
return new Promise((resolve) => {
const factory = this.componentFactoryResolver.resolveComponentFactory(ConfirmationDialogComponent);
this.dialogComponentRef = factory.create(this.injector);
this.dialogComponentRef.instance.message = message;
this.dialogComponentRef.instance.confirm.subscribe((result) => {
resolve(result);
this.close();
});
this.appRef.attachView(this.dialogComponentRef.hostView);
const domElem = this.dialogComponentRef.hostView.rootNodes[0];
document.body.appendChild(domElem);
});
}
close() {
if (this.dialogComponentRef) {
this.appRef.detachView(this.dialogComponentRef.hostView);
this.dialogComponentRef.destroy();
this.dialogComponentRef = null;
}
}
};
ConfirmationService.ctorParameters = () => [
{ type: ApplicationRef },
{ type: ComponentFactoryResolver },
{ type: Injector }
];
ConfirmationService.ɵprov = ɵɵdefineInjectable({ factory: function ConfirmationService_Factory() { return new ConfirmationService(ɵɵinject(ApplicationRef), ɵɵinject(ComponentFactoryResolver), ɵɵinject(INJECTOR)); }, token: ConfirmationService, providedIn: "root" });
ConfirmationService = __decorate([
Injectable({
providedIn: 'root'
})
], ConfirmationService);
let LocaleService = class LocaleService {
constructor() {
this.localeSubject = new BehaviorSubject({});
this.locale$ = this.localeSubject.asObservable();
}
setLocale(locale) {
this.localeSubject.next(locale);
}
getCurrentLocale() {
return this.localeSubject.value;
}
};
LocaleService.ɵprov = ɵɵdefineInjectable({ factory: function LocaleService_Factory() { return new LocaleService(); }, token: LocaleService, providedIn: "root" });
LocaleService = __decorate([
Injectable({
providedIn: 'root'
})
], LocaleService);
let UIComponent = class UIComponent {
constructor(uiFormService, formDataService, confirmationService, localeService) {
this.uiFormService = uiFormService;
this.formDataService = formDataService;
this.confirmationService = confirmationService;
this.localeService = localeService;
this.enableGeneralFields = true;
this.enableConditionalLogicBlocks = false;
this.isSurvey = true;
this.incomingFormData = {
formData: {
steps: [],
generalFields: []
},
options: {
name: '',
type: '',
country: ''
},
uniqueFormData: []
};
this.enableSetValidationOptions = false;
this.locale = {};
this.saveClicked = new EventEmitter();
this.formFieldType = FormFieldType;
this.formData = {
formData: {
steps: [],
generalFields: []
},
options: {
name: '',
type: '',
country: ''
},
uniqueFormData: []
};
this.addedFields = [];
this.generalFields = [];
this.conditionalLogicBlocks = [];
this.fieldLabels = fieldTypesNames;
this.currentStep = 0;
this.usedFieldTypes = [];
this.isSidebarOpen = false;
this.isFieldsInsertingOpen = false;
this.isFieldPropertiesOpen = false;
this.isGeneral = false;
this.fieldToEdit = { id: '', name: '' };
this.hasFeedBackText = false;
this.editedFieldId = null;
}
ngOnInit() {
this.insertFormData();
this.createForm();
this.initializeUsedFieldTypes(this.addedFields);
this.updateSpecialFieldStates();
if (this.locale) {
this.localeService.setLocale(this.locale);
}
}
ngOnChanges(changes) {
if (changes['locale'] && this.locale) {
this.localeService.setLocale(this.locale);
}
}
createForm() {
this.dynamicForm = this.uiFormService.createFormGroup(this.addedFields);
if (this.enableGeneralFields) {
this.generalForm = this.uiFormService.createFormGroup(this.generalFields);
}
}
removeField(field, isGeneral) {
let fieldsArray;
let formGroup;
if (isGeneral) {
fieldsArray = this.generalFields;
formGroup = this.generalForm;
}
else {
fieldsArray = this.addedFields;
formGroup = this.dynamicForm;
}
const index = fieldsArray.indexOf(field);
if (index !== -1) {
fieldsArray.splice(index, 1);
formGroup.removeControl(field.id);
}
if (this.isFieldUnique(field.type)) {
const typeIndex = this.usedFieldTypes.indexOf(field.type);
if (typeIndex !== -1) {
this.usedFieldTypes.splice(typeIndex, 1);
}
}
this.saveCurrentStepData();
}
copyField(field, isGeneral) {
let fieldsArray;
let formGroup;
if (isGeneral) {
fieldsArray = this.generalFields;
formGroup = this.generalForm;
}
else {
fieldsArray = this.addedFields;
formGroup = this.dynamicForm;
}
const copiedField = Object.assign({}, field);
copiedField.id = this.uiFormService.generateUniqueId();
if (copiedField.feedBackText) {
copiedField.feedBackText = false;
}
const originalFieldIndex = fieldsArray.indexOf(field);
fieldsArray.splice(originalFieldIndex + 1, 0, copiedField);
const newFormControl = this.uiFormService.createControl();
formGroup.addControl(copiedField.id, newFormControl);
this.saveCurrentStepData();
}
insertConditionalLogicBlock() {
const newBlock = defaultConditionalLogicBlock;
this.conditionalLogicBlocks.push(newBlock);
this.saveCurrentStepData();
}
removeConditionalLogicBlock(index) {
this.conditionalLogicBlocks.splice(index, 1);
this.saveCurrentStepData();
}
goToStep(step) {
this.currentStep = step;
if (!this.formData.formData.steps[this.currentStep]) {
this.formData.formData.steps[this.currentStep] = {
addedFields: [],
conditionalLogicBlocks: []
};
}
this.updateStep();
}
goToNextStep() {
if (this.addedFields.length === 0) {
return;
}
this.saveCurrentStepData();
this.currentStep++;
this.goToStep(this.currentStep);
}
goToPreviousStep() {
if (this.currentStep > 0) {
this.saveCurrentStepData();
this.currentStep--;
this.goToStep(this.currentStep);
}
}
saveCurrentStepData() {
this.formData.formData.generalFields = [...this.generalFields];
this.formData.formData.steps[this.currentStep] = {
addedFields: [...this.addedFields],
conditionalLogicBlocks: [...this.conditionalLogicBlocks]
};
this.updateSpecialFieldStates();
}
updateSpecialFieldStates() {
var _a;
const needContactField = this.findFieldInSteps(FormFieldType.NeedContact);
this.needContactDefaultValue = (_a = needContactField) === null || _a === void 0 ? void 0 : _a.defaultValue;
if (needContactField &&
needContactField.defaultValue !== undefined &&
!this.enableSetValidationOptions) {
this.updateRequiredFields(needContactField.defaultValue);
}
this.updateFeedBackTextFields();
}
updateFeedBackTextFields() {
this.hasFeedBackText = false;
for (const step of this.formData.formData.steps) {
for (const field of step.addedFields) {
if (field.type &&
[FormFieldType.Text, FormFieldType.Textarea].includes(field.type) &&
field.feedBackText) {
this.hasFeedBackText = true;
break;
}
}
if (this.hasFeedBackText)
break;
}
}
findFieldInSteps(fieldType) {
for (const step of this.formData.formData.steps) {
const field = step.addedFields.find((f) => f.type === fieldType);
if (field) {
return field;
}
}
return undefined;
}
updateRequiredFields(value) {
const isRequired = value === '1';
for (const step of this.formData.formData.steps) {
step.addedFields.forEach((field) => {
if (field.type &&
[
FormFieldType.ContactName,
FormFieldType.ContactSurname,
FormFieldType.ContactEmail
].includes(field.type)) {
field.required = isRequired;
}
});
}
}
insertFormData() {
var _a, _b, _c, _d, _e;
const incomingFormData = this.incomingFormData;
if (incomingFormData) {
this.formData = incomingFormData;
}
else {
this.formData = {
formData: {
steps: [],
generalFields: []
},
options: {
name: '',
type: '',
country: ''
},
uniqueFormData: []
};
}
this.addedFields = (_b = (_a = this.formData.formData.steps[this.currentStep]) === null || _a === void 0 ? void 0 : _a.addedFields, (_b !== null && _b !== void 0 ? _b : []));
this.generalFields = (_c = this.formData.formData.generalFields, (_c !== null && _c !== void 0 ? _c : []));
this.conditionalLogicBlocks = (_e = (_d = this.formData.formData.steps[this.currentStep]) === null || _d === void 0 ? void 0 : _d.conditionalLogicBlocks, (_e !== null && _e !== void 0 ? _e : []));
}
saveForm() {
this.saveCurrentStepData();
const payload = this.formDataService.prepareFormData(this.formData);
this.formDataService.setFormData(payload);
this.saveClicked.emit(payload);
}
preventDefault(event) {
event.preventDefault();
}
onDrop(event) {
const formArray = event.container.data;
moveItemInArray(formArray, event.previousIndex, event.currentIndex);
this.saveCurrentStepData();
}
copyStep(index) {
if (this.addedFields.length === 0) {
return;
}
const copiedStep = Object.assign({}, this.formData.formData.steps[index]);
copiedStep.addedFields = copiedStep.addedFields.map((field) => (Object.assign(Object.assign({}, field), { id: this.uiFormService.generateUniqueId() })));
this.formData.formData.steps.splice(index + 1, 0, copiedStep);
this.goToStep(index + 1);
this.saveCurrentStepData();
}
moveStep(index, direction) {
if (direction === 'next') {
[this.formData.formData.steps[index], this.formData.formData.steps[index + 1]] = [
this.formData.formData.steps[index + 1],
this.formData.formData.steps[index]
];
}
else if (direction === 'prev') {
[this.formData.formData.steps[index], this.formData.formData.steps[index - 1]] = [
this.formData.formData.steps[index - 1],
this.formData.formData.steps[index]
];
}
this.goToStep(index);
this.saveCurrentStepData();
}
deleteStepConfirmation(index) {
const localizedMessage = this.localeService.getCurrentLocale()['Are you sure you want to delete this step?'] ||
'Are you sure you want to delete this step?';
this.confirmationService.open(localizedMessage).then((result) => {
if (result) {
this.deleteStep(index);
}
});
}
deleteStep(index) {
this.formData.formData.steps.splice(index, 1);
if (this.currentStep >= index) {
this.currentStep = this.currentStep > 0 ? this.currentStep - 1 : 0;
}
if (this.formData.formData.steps.length > 0) {
this.updateStep();
}
else {
this.addedFields = [];
this.conditionalLogicBlocks = [];
this.createForm();
}
this.saveCurrentStepData();
}
updateStep() {
this.addedFields = this.formData.formData.steps[this.currentStep].addedFields;
this.conditionalLogicBlocks =
this.formData.formData.steps[this.currentStep].conditionalLogicBlocks;
this.createForm();
}
isFieldUnique(fieldType) {
return uniqueFieldTypes.includes(fieldType);
}
toggleSidebar() {
this.isSidebarOpen = this.isFieldsInsertingOpen || this.isFieldPropertiesOpen;
}
toggleFieldsInsertingSidebar(isGeneral = false) {
this.isFieldsInsertingOpen = !this.isFieldsInsertingOpen;
this.isFieldPropertiesOpen = false;
this.isGeneral = isGeneral;
this.toggleSidebar();
}
toggleFieldPropertiesSidebar(field, isGeneral) {
this.isFieldPropertiesOpen = !this.isFieldPropertiesOpen;
this.isFieldsInsertingOpen = false;
this.toggleSidebar();
this.fieldToEdit = field;
this.isGeneral = isGeneral;
this.editedFieldId = this.isFieldPropertiesOpen ? field.id : null;
if (!this.isSidebarOpen) {
this.editedFieldId = null;
}
}
onPropertiesSave(selectedField) {
if (selectedField) {
const newFormControl = this.uiFormService.createControl(selectedField.defaultValue);
if (this.isGeneral) {
this.generalFields.push(selectedField);
this.generalForm.addControl(selectedField.id, newFormControl);
}
else {
this.addedFields.push(selectedField);
this.dynamicForm.addControl(selectedField.id, newFormControl);
}
this.addUsedFieldType(selectedField.type);
this.saveCurrentStepData();
}
}
onPropertiesSaveAfterEdit(updatedField) {
if (updatedField) {
let fieldsArray;
if (this.isGeneral) {
fieldsArray = this.generalFields;
}
else {
fieldsArray = this.addedFields;
}
const index = fieldsArray.indexOf(this.fieldToEdit);
if (index !== -1) {
if (updatedField.step !== this.currentStep) {
fieldsArray.splice(index, 1);
const newStepIndex = updatedField.step;
const targetStep = this.formData.formData.steps[newStepIndex];
targetStep.addedFields.push(updatedField);
}
else {
fieldsArray[index] = updatedField;
}
}
if (updatedField.defaultValue !== undefined) {
const formControl = this.dynamicForm.get(updatedField.id);
if (formControl) {
formControl.setValue(updatedField.defaultValue);
}
}
this.saveCurrentStepData();
}
}
addUsedFieldType(type) {
if (this.isFieldUnique(type) && !this.usedFieldTypes.includes(type)) {
this.usedFieldTypes.push(type);
}
}
initializeUsedFieldTypes(fields) {
fields.forEach((field) => {
this.addUsedFieldType(field.type);
});
}
};
UIComponent.ctorParameters = () => [
{ type: UiFormService },
{ type: FormDataService },
{ type: ConfirmationService },
{ type: LocaleService }
];
__decorate([
Input()
], UIComponent.prototype, "enableGeneralFields", void 0);
__decorate([
Input()
], UIComponent.prototype, "enableConditionalLogicBlocks", void 0);
__decorate([
Input()
], UIComponent.prototype, "isSurvey", void 0);
__decorate([
Input()
], UIComponent.prototype, "incomingFormData", void 0);
__decorate([
Input()
], UIComponent.prototype, "enableSetValidationOptions", void 0);
__decorate([
Input()
], UIComponent.prototype, "locale", void 0);
__decorate([
Output()
], UIComponent.prototype, "saveClicked", void 0);
UIComponent = __decorate([
Component({
selector: 'app-ui',
template: "<div class=\"form-builder__test-form\">\r\n <div class=\"form-builder__test-form__main\" [ngClass]=\"{ collapsed: isSidebarOpen }\">\r\n <div class=\"form-builder__test-form__body\">\r\n <form\r\n *ngIf=\"enableGeneralFields === true\"\r\n [formGroup]=\"generalForm\"\r\n cdkDropList\r\n [cdkDropListData]=\"generalFields\"\r\n (cdkDropListDropped)=\"onDrop($event)\">\r\n <h3 *ngIf=\"generalFields.length\" class=\"form-builder__test-form__step\">\r\n {{ \"General Fields\" | localized }}\r\n </h3>\r\n <ng-container *ngFor=\"let field of generalFields\">\r\n <div\r\n class=\"form-builder__test-form__block\"\r\n [class.form-builder__test-form__block--editing]=\"field.id === editedFieldId\"\r\n cdkDrag>\r\n <div class=\"form-builder__test-form__label\">\r\n <label class=\"form-builder__form-label\">{{ field.title }}</label>\r\n </div>\r\n <div class=\"form-builder__test-form__field\" [ngSwitch]=\"field.type\">\r\n <div class=\"form-builder__test-form__field-label\">\r\n <span>{{ fieldLabels[field.type] | localized }}</span>\r\n </div>\r\n <div class=\"form-builder__test-form__field-toolbar\">\r\n <span\r\n class=\"form-builder__test-form__field-toolbar-icon\"\r\n [attr.data-tooltip]=\"'Delete' | localized\"\r\n (click)=\"removeField(field, true)\"\r\n ><i class=\"form-builder__bi form-builder__bi-trash\"></i\r\n ></span>\r\n <span\r\n class=\"form-builder__test-form__field-toolbar-icon\"\r\n [attr.data-tooltip]=\"'Edit' | localized\"\r\n (click)=\"toggleFieldPropertiesSidebar(field, true)\"\r\n ><i class=\"form-builder__bi form-builder__bi-pencil\"></i\r\n ></span>\r\n <span\r\n class=\"form-builder__test-form__field-toolbar-icon\"\r\n [attr.data-tooltip]=\"'Copy' | localized\"\r\n (click)=\"copyField(field, true)\"\r\n ><i class=\"form-builder__bi form-builder__bi-files\"></i\r\n ></span>\r\n <span\r\n class=\"form-builder__test-form__field-toolbar-icon form-builder__test-form__field-toolbar-icon--move\"\r\n [attr.data-tooltip]=\"'Move' | localized\"\r\n cdkDragHandle>\r\n <i class=\"form-builder__bi form-builder__bi-arrows-move\"></i>\r\n </span>\r\n </div>\r\n\r\n <ng-container *ngSwitchCase=\"formFieldType.Select\">\r\n <select\r\n [id]=\"field.id\"\r\n [formControlName]=\"field.id\"\r\n (mousedown)=\"preventDefault($event)\"\r\n class=\"form-builder__form-select\">\r\n <option value=\"\">{{ field.placeholder }}</option>\r\n <option *ngFor=\"let option of field.options\" [value]=\"option.value\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"formFieldType.Checkbox\">\r\n <input\r\n [id]=\"field.id\"\r\n type=\"checkbox\"\r\n [formControlName]=\"field.id\"\r\n class=\"form-builder__form-check-input\" />\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <input\r\n [id]=\"field.id\"\r\n [type]=\"field.type\"\r\n [formControlName]=\"field.id\"\r\n class=\"form-builder__form-control\"\r\n (mousedown)=\"preventDefault($event)\"\r\n (click)=\"preventDefault($event)\" />\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </form>\r\n <form\r\n [formGroup]=\"dynamicForm\"\r\n cdkDropList\r\n [cdkDropListData]=\"addedFields\"\r\n (cdkDropListDropped)=\"onDrop($event)\">\r\n <h3 class=\"form-builder__test-form__step\">\r\n {{ \"Step\" | localized }} {{ currentStep + 1 }}\r\n </h3>\r\n <ng-container *ngFor=\"let field of addedFields\">\r\n <div\r\n class=\"form-builder__test-form__block\"\r\n [class.form-builder__test-form__block--editing]=\"field.id === editedFieldId\"\r\n cdkDrag>\r\n <div\r\n class=\"form-builder__test-form__label\"\r\n *ngIf=\"\r\n field.type !== formFieldType.Likert &&\r\n field.type !== formFieldType.Csat &&\r\n field.type !== formFieldType.CES &&\r\n field.type !== formFieldType.QE &&\r\n field.type !== formFieldType.NPS\r\n \">\r\n <label class=\"form-builder__form-label\">{{ field.title }}</label>\r\n </div>\r\n <div class=\"form-builder__test-form__field\" [ngSwitch]=\"field.type\">\r\n <div class=\"form-builder__test-form__field-label\">\r\n <span\r\n *ngIf=\"field