UNPKG

@skireal/form-builder

Version:
1,047 lines (1,038 loc) 157 kB
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