ui-core-abv 0.6.76 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { CommonModule, CurrencyPipe, DatePipe } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { Input, Component, EventEmitter, Output, Directive, forwardRef, InjectionToken, Optional, Inject, Injectable, inject, ChangeDetectorRef, Pipe, ViewContainerRef, ElementRef, ViewChild, createComponent, HostListener, ViewChildren, Host, DestroyRef, signal, computed, effect, TemplateRef, ContentChild, Injector, EnvironmentInjector, HostBinding, input, output } from '@angular/core';
5
5
  import * as i1$1 from '@angular/forms';
6
- import { NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
6
+ import { NG_VALUE_ACCESSOR, FormsModule, FormBuilder, Validators, ReactiveFormsModule, FormArray } from '@angular/forms';
7
7
  import { BehaviorSubject, Subject, debounceTime, distinctUntilChanged, tap, switchMap, of, finalize, map, isObservable, from, Subscription } from 'rxjs';
8
8
  import { HttpClient } from '@angular/common/http';
9
9
  import { Overlay, OverlayRef } from '@angular/cdk/overlay';
@@ -388,6 +388,8 @@ const DICTIONARY_EN = {
388
388
  block_subtitle: 'Subtitle',
389
389
  add_block: 'Add block',
390
390
  delete_block: 'Delete block',
391
+ delete_block_dependency_blocked: 'This block cannot be deleted because it contains fields that other fields depend on',
392
+ delete_field_dependency_blocked: 'This field cannot be deleted because another field depends on its value',
391
393
  preview_form: 'Preview',
392
394
  submit_form: 'Submit',
393
395
  select_field_to_edit: 'Select a field to edit its properties',
@@ -486,6 +488,13 @@ const DICTIONARY_EN = {
486
488
  selectSearchEnabled_tip: 'Enables search among options',
487
489
  selectNullable: 'Allow empty option',
488
490
  selectSearchEnabled: 'Enable search',
491
+ repeaterMinItems: 'Minimum items',
492
+ repeaterMinItems_tip: 'Minimum number of required items',
493
+ repeaterMaxItems: 'Maximum items',
494
+ repeaterMaxItems_tip: 'Maximum number of allowed items',
495
+ repeaterAddLabel: 'Add button label',
496
+ repeaterRemoveLabel: 'Remove button label',
497
+ repeaterItemTitle: 'Item title',
489
498
  },
490
499
  options_editor: {
491
500
  title: 'Options',
@@ -511,10 +520,25 @@ const DICTIONARY_EN = {
511
520
  delete: 'Delete',
512
521
  show_advanced: 'Show advanced',
513
522
  hide_advanced: 'Hide advanced',
523
+ subfields: 'Sub-fields',
524
+ },
525
+ visibility_operators: {
526
+ equals: 'Equals',
527
+ notEquals: 'Does not equal',
528
+ greaterThan: 'Is greater than',
529
+ lessThan: 'Is less than',
530
+ includes: 'Includes',
531
+ notIncludes: 'Does not include',
532
+ isEmpty: 'Is empty',
533
+ isNotEmpty: 'Is not empty',
514
534
  },
515
535
  validation: {
516
536
  duplicate_identifier: 'The identifier "{{identifier}}" is used more than once.',
517
537
  duplicate_identifier_title: 'Validation error',
538
+ },
539
+ repeater: {
540
+ no_subfields: 'No sub-fields yet. Add one below.',
541
+ add_subfield: 'Add sub-field',
518
542
  }
519
543
  },
520
544
  validation: {
@@ -691,6 +715,8 @@ const DICTIONARY_ES = {
691
715
  block_subtitle: 'Subtítulo',
692
716
  add_block: 'Agregar bloque',
693
717
  delete_block: 'Eliminar bloque',
718
+ delete_block_dependency_blocked: 'Este bloque no se puede eliminar porque contiene campos de los que dependen otros campos',
719
+ delete_field_dependency_blocked: 'Este campo no se puede eliminar porque otro campo depende de su valor',
694
720
  preview_form: 'Vista previa',
695
721
  submit_form: 'Guardar',
696
722
  select_field_to_edit: 'Selecciona un campo para editar sus propiedades',
@@ -773,22 +799,29 @@ const DICTIONARY_ES = {
773
799
  iaValidation: 'Validación IA',
774
800
  iaValidationPrompt: 'Prompt de validación IA',
775
801
  searchApi_tip: 'URL para obtener opciones de búsqueda',
776
- optionsSourceKey: 'Fuente de opciones',
777
- optionsSourceKey_tip: 'Clave lógica registrada por la aplicación consumidora',
778
- optionsSourceIdField: 'Campo ID de opción',
779
- optionsSourceIdField_tip: 'Ruta del valor usado como id. Ejemplo: id',
780
- optionsSourceTextField: 'Campo texto de opción',
781
- optionsSourceTextField_tip: 'Ruta del valor usado como texto. Ejemplo: name',
802
+ optionsSourceKey: 'Origen de opciones',
803
+ optionsSourceKey_tip: 'Opciones manuales o API externa',
804
+ optionsSourceIdField: 'Nombre de campo ID',
805
+ optionsSourceIdField_tip: 'Propiedad del objeto para usar como ID',
806
+ optionsSourceTextField: 'Nombre de campo de texto',
807
+ optionsSourceTextField_tip: 'Propiedad del objeto para mostrar',
782
808
  optionsSourceTextTemplate: 'Plantilla de texto',
783
809
  optionsSourceTextTemplate_tip: 'Ejemplo: {{name}} ({{id}})',
784
810
  optionsSourceDependsOn: 'Depende de',
785
- optionsSourceDependsOn_tip: 'Identificador del campo padre que dispara la recarga',
811
+ optionsSourceDependsOn_tip: 'Campo para filtrar opciones según valor',
786
812
  optionsSourceParamName: 'Nombre del parámetro',
787
813
  optionsSourceParamName_tip: 'Nombre con el que se envía el valor dependiente',
788
814
  selectNullable_tip: 'Permite seleccionar una opción vacía',
789
815
  selectSearchEnabled_tip: 'Habilita búsqueda entre opciones',
790
816
  selectNullable: 'Permitir opción vacía',
791
817
  selectSearchEnabled: 'Habilitar búsqueda',
818
+ repeaterMinItems: 'Mínimo de ítems',
819
+ repeaterMinItems_tip: 'Cantidad mínima de ítems requerida',
820
+ repeaterMaxItems: 'Máximo de ítems',
821
+ repeaterMaxItems_tip: 'Cantidad máxima de ítems permitida',
822
+ repeaterAddLabel: 'Etiqueta del botón agregar',
823
+ repeaterRemoveLabel: 'Etiqueta del botón eliminar',
824
+ repeaterItemTitle: 'Título de ítem',
792
825
  },
793
826
  options_editor: {
794
827
  title: 'Opciones',
@@ -814,10 +847,25 @@ const DICTIONARY_ES = {
814
847
  delete: 'Eliminar',
815
848
  show_advanced: 'Mostrar avanzado',
816
849
  hide_advanced: 'Ocultar avanzado',
850
+ subfields: 'Sub-campos',
851
+ },
852
+ visibility_operators: {
853
+ equals: 'Es igual a',
854
+ notEquals: 'No es igual a',
855
+ greaterThan: 'Es mayor que',
856
+ lessThan: 'Es menor que',
857
+ includes: 'Incluye',
858
+ notIncludes: 'No incluye',
859
+ isEmpty: 'Está vacío',
860
+ isNotEmpty: 'No está vacío',
817
861
  },
818
862
  validation: {
819
863
  duplicate_identifier: 'El identificador "{{identifier}}" se está usando más de una vez.',
820
864
  duplicate_identifier_title: 'Error de validación',
865
+ },
866
+ repeater: {
867
+ no_subfields: 'Sin sub-campos. Añade uno abajo.',
868
+ add_subfield: 'Agregar sub-campo',
821
869
  }
822
870
  },
823
871
  validation: {
@@ -3848,6 +3896,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
3848
3896
  type: Injectable
3849
3897
  }], ctorParameters: () => [] });
3850
3898
 
3899
+ class UicRepeaterComponent {
3900
+ fb = inject(FormBuilder);
3901
+ formArray;
3902
+ field;
3903
+ cols = 2;
3904
+ error;
3905
+ ngOnInit() {
3906
+ if (this.formArray.length === 0) {
3907
+ const min = this.field.repeaterMinItems ?? 0;
3908
+ for (let i = 0; i < min; i++) {
3909
+ this.addGroup();
3910
+ }
3911
+ }
3912
+ }
3913
+ getGroup(index) {
3914
+ return this.formArray.at(index);
3915
+ }
3916
+ addGroup() {
3917
+ if (this.field.repeaterMaxItems && this.formArray.length >= this.field.repeaterMaxItems) {
3918
+ return;
3919
+ }
3920
+ const group = this.fb.group({});
3921
+ (this.field.repeaterFields || []).forEach(subField => {
3922
+ const validators = this.mapValidatorsFromField(subField);
3923
+ const controlState = {
3924
+ value: this.getBlankValue(subField),
3925
+ disabled: !!subField.disabled
3926
+ };
3927
+ group.addControl(subField.name, this.fb.control(controlState, validators));
3928
+ });
3929
+ this.formArray.push(group);
3930
+ }
3931
+ removeGroup(index) {
3932
+ if (this.field.repeaterMinItems && this.formArray.length <= this.field.repeaterMinItems) {
3933
+ return;
3934
+ }
3935
+ this.formArray.removeAt(index);
3936
+ }
3937
+ get canAdd() {
3938
+ if (!this.field.repeaterMaxItems)
3939
+ return true;
3940
+ return this.formArray.length < this.field.repeaterMaxItems;
3941
+ }
3942
+ get canRemove() {
3943
+ if (this.field.repeaterMinItems !== undefined && this.field.repeaterMinItems !== null) {
3944
+ return this.formArray.length > this.field.repeaterMinItems;
3945
+ }
3946
+ return true;
3947
+ }
3948
+ mapValidatorsFromField(field) {
3949
+ const validators = [];
3950
+ if (field.required)
3951
+ validators.push(Validators.required);
3952
+ if (typeof field.min === 'number')
3953
+ validators.push(Validators.min(field.min));
3954
+ if (typeof field.max === 'number')
3955
+ validators.push(Validators.max(field.max));
3956
+ if (typeof field.maxLength === 'number')
3957
+ validators.push(Validators.maxLength(field.maxLength));
3958
+ if (typeof field.minLength === 'number')
3959
+ validators.push(Validators.minLength(field.minLength));
3960
+ if (field.pattern instanceof RegExp)
3961
+ validators.push(Validators.pattern(field.pattern));
3962
+ return validators;
3963
+ }
3964
+ getBlankValue(field) {
3965
+ switch (field.type) {
3966
+ case 'text':
3967
+ case 'textarea':
3968
+ case 'phone':
3969
+ return '';
3970
+ case 'checkbox':
3971
+ case 'switch':
3972
+ return false;
3973
+ case 'multyselect':
3974
+ return [];
3975
+ case 'slider':
3976
+ return field.min ?? 0;
3977
+ default:
3978
+ return null;
3979
+ }
3980
+ }
3981
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicRepeaterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3982
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicRepeaterComponent, isStandalone: true, selector: "ui-repeater", inputs: { formArray: "formArray", field: "field", cols: "cols", error: "error" }, ngImport: i0, template: "<div class=\"repeater-label\"> {{field.label}} </div>\r\n<div class=\"uic-repeater\" [class.disabled]=\"formArray.disabled\">\r\n @for (group of formArray.controls; track $index) {\r\n <div class=\"uic-repeater-item\">\r\n <div class=\"uic-repeater-item-header\">\r\n <h4 class=\"uic-repeater-item-title\">\r\n {{ field.repeaterItemTitle || field.label || 'Item' }} {{ $index + 1 }}\r\n </h4>\r\n @if (canRemove && !formArray.disabled) {\r\n <ui-button \r\n [icon]=\"'ri-delete-bin-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'red'\" \r\n size=\"s\" \r\n (click)=\"removeGroup($index)\"\r\n [text]=\"field.repeaterRemoveLabel || 'Eliminar'\">\r\n </ui-button>\r\n }\r\n </div>\r\n <div class=\"uic-repeater-item-body\">\r\n <ui-dynamic-form \r\n [form]=\"getGroup($index)\" \r\n [fields]=\"field.repeaterFields || []\" \r\n [disabled]=\"formArray.disabled\"\r\n [cols]=\"cols\"\r\n [isSubForm]=\"true\">\r\n </ui-dynamic-form>\r\n </div>\r\n </div>\r\n }\r\n \r\n @if (canAdd && !formArray.disabled) {\r\n <div class=\"uic-repeater-add-action\">\r\n <ui-button \r\n [icon]=\"'ri-add-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'primary'\" \r\n (click)=\"addGroup()\"\r\n [text]=\"field.repeaterAddLabel || 'A\u00F1adir'\">\r\n </ui-button>\r\n </div>\r\n }\r\n @if (error) {\r\n <p class=\"uic-repeater-error\">{{ error }}</p>\r\n }\r\n</div>\r\n", styles: [".uic-repeater{display:flex;flex-direction:column;gap:16px;width:100%}.uic-repeater.disabled{opacity:.7;pointer-events:none}.uic-repeater .uic-repeater-item{border:1px solid var(--primary-400, #e0e0e0);border-radius:8px;padding:16px;background-color:var(--uic-bg-color, #fff)}.uic-repeater .uic-repeater-item .uic-repeater-item-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;padding-bottom:12px;border-bottom:1px dashed var(--uic-border-color, #e0e0e0)}.uic-repeater .uic-repeater-item .uic-repeater-item-header .uic-repeater-item-title{margin:0;font-size:14px;font-weight:600;color:var(--uic-text-color, #333)}.uic-repeater .uic-repeater-item .uic-repeater-item-body{width:100%}.uic-repeater .uic-repeater-add-action{margin-top:8px}.uic-repeater .uic-repeater-error{margin:4px 0 0;font-size:12px;color:var(--uic-red, #e53935)}.repeater-label{font-size:max(var(--form-ref) + 4px,14px);line-height:1.125rem;min-height:1.125rem;font-weight:400;color:var(--grey-950);margin-bottom:var(--input-label-space)}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "component", type: i0.forwardRef(() => UicButtonComponent), selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "component", type: i0.forwardRef(() => UicDynamicFormComponent), selector: "ui-dynamic-form", inputs: ["fields", "isSubForm", "form", "disabled", "voiceToTextSilenceMs", "cols", "fileUidResolverFn", "selectOptionsResolver"] }] });
3983
+ }
3984
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicRepeaterComponent, decorators: [{
3985
+ type: Component,
3986
+ args: [{ selector: 'ui-repeater', standalone: true, imports: [
3987
+ CommonModule,
3988
+ ReactiveFormsModule,
3989
+ UicButtonComponent,
3990
+ forwardRef(() => UicDynamicFormComponent)
3991
+ ], template: "<div class=\"repeater-label\"> {{field.label}} </div>\r\n<div class=\"uic-repeater\" [class.disabled]=\"formArray.disabled\">\r\n @for (group of formArray.controls; track $index) {\r\n <div class=\"uic-repeater-item\">\r\n <div class=\"uic-repeater-item-header\">\r\n <h4 class=\"uic-repeater-item-title\">\r\n {{ field.repeaterItemTitle || field.label || 'Item' }} {{ $index + 1 }}\r\n </h4>\r\n @if (canRemove && !formArray.disabled) {\r\n <ui-button \r\n [icon]=\"'ri-delete-bin-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'red'\" \r\n size=\"s\" \r\n (click)=\"removeGroup($index)\"\r\n [text]=\"field.repeaterRemoveLabel || 'Eliminar'\">\r\n </ui-button>\r\n }\r\n </div>\r\n <div class=\"uic-repeater-item-body\">\r\n <ui-dynamic-form \r\n [form]=\"getGroup($index)\" \r\n [fields]=\"field.repeaterFields || []\" \r\n [disabled]=\"formArray.disabled\"\r\n [cols]=\"cols\"\r\n [isSubForm]=\"true\">\r\n </ui-dynamic-form>\r\n </div>\r\n </div>\r\n }\r\n \r\n @if (canAdd && !formArray.disabled) {\r\n <div class=\"uic-repeater-add-action\">\r\n <ui-button \r\n [icon]=\"'ri-add-line'\" \r\n [type]=\"'bordered'\" \r\n [color]=\"'primary'\" \r\n (click)=\"addGroup()\"\r\n [text]=\"field.repeaterAddLabel || 'A\u00F1adir'\">\r\n </ui-button>\r\n </div>\r\n }\r\n @if (error) {\r\n <p class=\"uic-repeater-error\">{{ error }}</p>\r\n }\r\n</div>\r\n", styles: [".uic-repeater{display:flex;flex-direction:column;gap:16px;width:100%}.uic-repeater.disabled{opacity:.7;pointer-events:none}.uic-repeater .uic-repeater-item{border:1px solid var(--primary-400, #e0e0e0);border-radius:8px;padding:16px;background-color:var(--uic-bg-color, #fff)}.uic-repeater .uic-repeater-item .uic-repeater-item-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;padding-bottom:12px;border-bottom:1px dashed var(--uic-border-color, #e0e0e0)}.uic-repeater .uic-repeater-item .uic-repeater-item-header .uic-repeater-item-title{margin:0;font-size:14px;font-weight:600;color:var(--uic-text-color, #333)}.uic-repeater .uic-repeater-item .uic-repeater-item-body{width:100%}.uic-repeater .uic-repeater-add-action{margin-top:8px}.uic-repeater .uic-repeater-error{margin:4px 0 0;font-size:12px;color:var(--uic-red, #e53935)}.repeater-label{font-size:max(var(--form-ref) + 4px,14px);line-height:1.125rem;min-height:1.125rem;font-weight:400;color:var(--grey-950);margin-bottom:var(--input-label-space)}\n"] }]
3992
+ }], propDecorators: { formArray: [{
3993
+ type: Input,
3994
+ args: [{ required: true }]
3995
+ }], field: [{
3996
+ type: Input,
3997
+ args: [{ required: true }]
3998
+ }], cols: [{
3999
+ type: Input
4000
+ }], error: [{
4001
+ type: Input
4002
+ }] } });
4003
+
3851
4004
  class UicDynamicFormComponent {
3852
4005
  ngZone;
3853
4006
  fieldsSignal = signal([]);
@@ -3869,9 +4022,9 @@ class UicDynamicFormComponent {
3869
4022
  get fields() {
3870
4023
  return this.fieldsSignal();
3871
4024
  }
4025
+ isSubForm = false;
3872
4026
  set form(value) {
3873
4027
  this.formSignal.set(value);
3874
- this.formState.setForm(value);
3875
4028
  }
3876
4029
  get form() {
3877
4030
  return this.formSignal();
@@ -3906,6 +4059,12 @@ class UicDynamicFormComponent {
3906
4059
  return field.visibilityRules.every(rule => this.matchesVisibilityRule(rule));
3907
4060
  });
3908
4061
  });
4062
+ formSyncEffect = effect(() => {
4063
+ const form = this.formSignal();
4064
+ if (form && !this.isSubForm) {
4065
+ this.formState.setForm(form);
4066
+ }
4067
+ });
3909
4068
  fieldStateSyncEffect = effect(() => {
3910
4069
  this.visibleFieldStates();
3911
4070
  this.syncFieldEnabledState();
@@ -3965,7 +4124,14 @@ class UicDynamicFormComponent {
3965
4124
  const isVisible = visibleNames.has(field.name);
3966
4125
  const wasVisible = this.fieldVisibilityState.get(field.name);
3967
4126
  if (!isVisible && wasVisible) {
3968
- control.setValue(this.getClearValue(field.type), { emitEvent: false });
4127
+ if (field.type === 'repeater') {
4128
+ const arr = control;
4129
+ while (arr.length > 0)
4130
+ arr.removeAt(0, { emitEvent: false });
4131
+ }
4132
+ else {
4133
+ control.setValue(this.getClearValue(field.type), { emitEvent: false });
4134
+ }
3969
4135
  control.markAsPristine();
3970
4136
  control.markAsUntouched();
3971
4137
  }
@@ -3988,14 +4154,20 @@ class UicDynamicFormComponent {
3988
4154
  if (fieldType === 'checkbox' || fieldType === 'switch') {
3989
4155
  return false;
3990
4156
  }
3991
- if (fieldType === 'multyselect') {
4157
+ if (fieldType === 'multyselect' || fieldType === 'repeater') {
3992
4158
  return [];
3993
4159
  }
3994
4160
  return null;
3995
4161
  }
3996
4162
  matchesVisibilityRule(rule) {
3997
4163
  const currentValue = this.getValueByFieldName(rule.fieldName);
4164
+ const dependencyField = this.findFieldByName(rule.fieldName);
4165
+ const dependencyType = dependencyField?.type;
3998
4166
  switch (rule.operator) {
4167
+ case 'isEmpty':
4168
+ return this.isEmptyValue(currentValue);
4169
+ case 'isNotEmpty':
4170
+ return !this.isEmptyValue(currentValue);
3999
4171
  case 'equals': {
4000
4172
  if (typeof currentValue === 'boolean') {
4001
4173
  const normalizedRuleValue = this.parseBooleanLike(rule.value);
@@ -4003,7 +4175,7 @@ class UicDynamicFormComponent {
4003
4175
  return currentValue === normalizedRuleValue;
4004
4176
  }
4005
4177
  }
4006
- return currentValue === rule.value;
4178
+ return currentValue == rule.value;
4007
4179
  }
4008
4180
  case 'notEquals': {
4009
4181
  if (typeof currentValue === 'boolean') {
@@ -4012,27 +4184,33 @@ class UicDynamicFormComponent {
4012
4184
  return currentValue !== normalizedRuleValue;
4013
4185
  }
4014
4186
  }
4015
- return currentValue !== rule.value;
4187
+ return currentValue != rule.value;
4016
4188
  }
4017
4189
  case 'greaterThan':
4018
- return Number(currentValue) > Number(rule.value);
4190
+ return this.compareVisibilityValues(currentValue, rule.value, dependencyType) > 0;
4019
4191
  case 'lessThan':
4020
- return Number(currentValue) < Number(rule.value);
4192
+ return this.compareVisibilityValues(currentValue, rule.value, dependencyType) < 0;
4021
4193
  case 'includes':
4194
+ if (dependencyType === 'phone') {
4195
+ return this.getPhoneComparableValue(currentValue).includes(String(rule.value ?? ''));
4196
+ }
4022
4197
  if (currentValue == null)
4023
4198
  return false;
4024
4199
  if (Array.isArray(currentValue)) {
4025
- return currentValue.includes(rule.value);
4200
+ return this.arrayIncludesVisibilityValue(currentValue, rule.value);
4026
4201
  }
4027
4202
  if (typeof currentValue === 'string') {
4028
4203
  return currentValue.includes(rule.value);
4029
4204
  }
4030
4205
  return false;
4031
4206
  case 'notIncludes':
4207
+ if (dependencyType === 'phone') {
4208
+ return !this.getPhoneComparableValue(currentValue).includes(String(rule.value ?? ''));
4209
+ }
4032
4210
  if (currentValue == null)
4033
4211
  return true;
4034
4212
  if (Array.isArray(currentValue)) {
4035
- return !currentValue.includes(rule.value);
4213
+ return !this.arrayIncludesVisibilityValue(currentValue, rule.value);
4036
4214
  }
4037
4215
  if (typeof currentValue === 'string') {
4038
4216
  return !currentValue.includes(rule.value);
@@ -4042,6 +4220,61 @@ class UicDynamicFormComponent {
4042
4220
  return true;
4043
4221
  }
4044
4222
  }
4223
+ findFieldByName(fieldName) {
4224
+ return this.fieldsSignal().find(field => field.name === fieldName);
4225
+ }
4226
+ isEmptyValue(value) {
4227
+ if (value === null || value === undefined)
4228
+ return true;
4229
+ if (typeof value === 'string')
4230
+ return value.trim().length === 0;
4231
+ if (Array.isArray(value))
4232
+ return value.length === 0;
4233
+ if (typeof value === 'object')
4234
+ return Object.keys(value).length === 0;
4235
+ return false;
4236
+ }
4237
+ compareVisibilityValues(currentValue, ruleValue, fieldType) {
4238
+ const currentComparable = this.toComparableVisibilityValue(currentValue, fieldType);
4239
+ const ruleComparable = this.toComparableVisibilityValue(ruleValue, fieldType);
4240
+ if (currentComparable === null || ruleComparable === null) {
4241
+ return Number.NaN;
4242
+ }
4243
+ return currentComparable - ruleComparable;
4244
+ }
4245
+ toComparableVisibilityValue(value, fieldType) {
4246
+ if (value === null || value === undefined || value === '')
4247
+ return null;
4248
+ if (fieldType === 'date') {
4249
+ const timestamp = Date.parse(String(value));
4250
+ return Number.isNaN(timestamp) ? null : timestamp;
4251
+ }
4252
+ if (fieldType === 'time') {
4253
+ const match = String(value).match(/^(\d{1,2}):(\d{2})$/);
4254
+ if (!match)
4255
+ return null;
4256
+ const hours = Number(match[1]);
4257
+ const minutes = Number(match[2]);
4258
+ if (!Number.isFinite(hours) || !Number.isFinite(minutes))
4259
+ return null;
4260
+ return hours * 60 + minutes;
4261
+ }
4262
+ const numericValue = Number(value);
4263
+ return Number.isNaN(numericValue) ? null : numericValue;
4264
+ }
4265
+ getPhoneComparableValue(value) {
4266
+ if (!value)
4267
+ return '';
4268
+ if (typeof value === 'string')
4269
+ return value;
4270
+ if (typeof value === 'object') {
4271
+ return String(value.internationalNumber ?? '');
4272
+ }
4273
+ return '';
4274
+ }
4275
+ arrayIncludesVisibilityValue(values, ruleValue) {
4276
+ return values.some(value => value == ruleValue);
4277
+ }
4045
4278
  parseBooleanLike(value) {
4046
4279
  if (typeof value === 'boolean')
4047
4280
  return value;
@@ -4076,6 +4309,9 @@ class UicDynamicFormComponent {
4076
4309
  });
4077
4310
  });
4078
4311
  }
4312
+ asFormArray(control) {
4313
+ return control;
4314
+ }
4079
4315
  isListening(fieldName) {
4080
4316
  return this.listeningField === fieldName;
4081
4317
  }
@@ -4097,7 +4333,8 @@ class UicDynamicFormComponent {
4097
4333
  field: field.label ?? field.name,
4098
4334
  requiredLength: err.requiredLength,
4099
4335
  }),
4100
- pattern: (_err, field) => this.i18n.translate('validation.pattern', { field: field.label ?? field.name })
4336
+ pattern: (_err, field) => this.i18n.translate('validation.pattern', { field: field.label ?? field.name }),
4337
+ minLengthArray: (_err, field) => this.i18n.translate('validation.required', { field: field.label ?? field.name })
4101
4338
  };
4102
4339
  getInputColor(field, value) {
4103
4340
  const scale = field.internalColorScale;
@@ -4358,7 +4595,7 @@ class UicDynamicFormComponent {
4358
4595
  recognition.start();
4359
4596
  }
4360
4597
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicDynamicFormComponent, deps: [{ token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
4361
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicDynamicFormComponent, isStandalone: true, selector: "ui-dynamic-form", inputs: { fields: "fields", form: "form", disabled: "disabled", voiceToTextSilenceMs: "voiceToTextSilenceMs", cols: "cols", fileUidResolverFn: "fileUidResolverFn", selectOptionsResolver: "selectOptionsResolver" }, ngImport: i0, template: "<div class=\"form\" [ngStyle]=\"{'--cols': cols, '--min': '180px'}\" [formGroup]=\"form\">\r\n @for (state of visibleFieldStates(); track state.field.name) {\r\n @let field = state.field;\r\n @let control = state.control;\r\n @let currentValue = state.value ?? '';\r\n @let error = getControlErrorMessages(control, field)[0];\r\n\r\n <div [style.grid-column]=\"getGridColumn(field)\">\n @if (['text', 'number'].includes(field.type)) {\r\n @let inputColor = getInputColor(field, currentValue);\r\n <ui-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [disabled]=\"!!control?.disabled\"\r\n [internalIconColor]=\"inputColor\"\r\n [internalIcon]=\"field.internalIcon || (currentValue !== '' ? getIconByColor(field, inputColor) : '')\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\">\r\n <input #inp\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [type]=\"field.type\"\r\n [step]=\"field.step\"\r\n [min]=\"field.min\"\r\n [max]=\"field.max\"\r\n [minLength]=\"field.minLength||null\"\r\n [maxlength]=\"field.maxLength||null\"\r\n [placeholder]=\"field.placeholder??field.label\"/>\r\n @if (field.maxLength && field.showCounter) {\r\n <span counter>{{inp.value.length}} /{{field.maxLength}}</span>\r\n }\r\n </ui-input>\r\n }\r\n @if (field.type === 'textarea') {\r\n <ui-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [disabled]=\"!!control?.disabled\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\">\r\n <textarea #ta\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [minLength]=\"field.minLength||null\"\r\n [maxlength]=\"field.maxLength||null\"\r\n [textareaAutoresize]=\"field.textareaResize\"\r\n [textareaAutoresizeMinRows]=\"field.resizeMinRows??1\"\r\n [textareaAutoresizeMaxRows]=\"field.resizeMaxRows??5\"\r\n [rows]=\"field.resizeMinRows??3\"\r\n [placeholder]=\"field.placeholder??field.label\"\r\n ></textarea>\r\n @if (field.voiceToTextEnabled && !control?.disabled) {\r\n @let listening = isListening(field.name);\r\n <div class=\"speak-icon\" (click)=\"ta.focus()\">\r\n <ui-button\r\n [disabled]=\"listening\"\r\n style=\"margin-bottom: 2px;\"\r\n (click)=\"voiceToText(ta, field.name)\"\r\n [icon]=\"listening?'ri-mic-fill':'ri-mic-line'\"\r\n [type]=\"listening?'filled':'bordered'\"\r\n size=\"s\"\r\n [iconOnly]=\"true\"\r\n ></ui-button>\r\n @if (listening) {\r\n <i class=\"ri-voiceprint-line\"></i>\r\n }\r\n </div>\r\n }\r\n @if (field.showCounter) {\r\n <span counter>\r\n {{ta.value.length}}\r\n @if (field.maxLength) {\r\n /{{field.maxLength}}\r\n }\r\n </span>\r\n }\r\n </ui-input>\r\n }\r\n @if (field.type === 'phone') {\r\n <ui-phone-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder||field.label||''\"\r\n >\r\n </ui-phone-input>\r\n }\r\n @else if (field.type === 'date') {\r\n <ui-date-picker\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [justMonth]=\"!!field.monthMode\"\r\n [min]=\"field.minDate??''\"\r\n [max]=\"field.maxDate??''\"\r\n [monthDay]=\"field.monthDay||'first'\"\r\n [formControlName]=\"field.name\"\r\n ></ui-date-picker>\r\n }\r\n @else if (field.type === 'time') {\r\n <ui-time-picker\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [interval]=\"field.timeInterval??5\"\r\n [formControlName]=\"field.name\"\r\n ></ui-time-picker>\r\n }\r\n @else if (field.type === 'select') {\r\n <ui-select\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [loading]=\"field.loading??false\"\r\n [nullable]=\"field.selectNullable??false\"\r\n [searcherEnabled]=\"field.selectSearchEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-select>\r\n }\r\n @else if (field.type === 'multyselect') {\r\n <ui-multy-select\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-multy-select>\r\n }\r\n @else if (field.type === 'searcher') {\r\n <ui-searcher\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\"\r\n [searchFn]=\"getSearcherFn(field)\"\r\n [itemDisplayFn]=\"getSearcherDisplayFn(field)\"\r\n [itemIsEnabledFn]=\"getSearcherIsEnabledFn(field)\"\r\n [placeholder]=\"field.placeholder??'common.search_ellipsis'\"\r\n [error]=\"error\"\r\n ></ui-searcher>\r\n }\r\n @else if (field.type === 'file') {\r\n <ui-file-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [multy]=\"field.multyEnabled??false\"\n [tip]=\"field.tip??''\"\n [fileTypes]=\"field.fileTypes??null\"\n [fileUidResolverFn]=\"fileUidResolverFn\"\n [formControlName]=\"field.name\"\n ></ui-file-input>\n }\n @else if (['checkbox', 'switch'].includes(field.type)) {\r\n <ui-checkbox\r\n [label]=\"(field.label??'') + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [placeholder]=\"field.placeholder??''\"\r\n [tip]=\"field.tip??''\"\r\n [type]=\"field.type === 'checkbox' ? 'checkbox' : 'switch'\"\r\n ></ui-checkbox>\r\n }\r\n @else if (field.type === 'radio') {\r\n <ui-radio\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [tip]=\"field.tip??''\"\r\n [loading]=\"field.loading??false\"\r\n [error]=\"error\"\r\n [options]=\"field.options || []\"\r\n ></ui-radio>\r\n }\r\n @else if (field.type === 'slider') {\r\n <ui-slider\r\n [min]=\"field.min??0\"\r\n [max]=\"field.max??100\"\r\n [step]=\"field.sliderInterval??1\"\r\n [markerCount]=\"field.sliderMarks??5\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [tip]=\"field.tip??''\"\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n ></ui-slider>\r\n }\r\n @else if (field.type === 'pool') {\r\n <ui-pool-options\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [multy]=\"field.multyEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [listViewTitle]=\"field.poolTitle??'common.selected'\"\r\n [enabledListView]=\"field.poolEnabledListView||false\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-pool-options>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".form{width:100%;display:grid;align-items:stretch;gap:1rem;padding-bottom:.75rem}@media (max-width: 479px){.form{grid-template-columns:1fr}}@media (min-width: 768px){.form{grid-template-columns:repeat(var(--cols),minmax(0,1fr))}}@media (min-width: 480px) and (max-width: 767px){.form{grid-template-columns:repeat(min(var(--cols),2),minmax(0,1fr))}}.form>*{min-width:0}h2{margin-top:1.5rem}.speak-icon{display:flex;width:100%;align-items:center;gap:10px}.speak-icon i{font-size:20px;color:var(--primary-500)}textarea{resize:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: UicDatePickerComponent, selector: "ui-date-picker", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "disabled", "label", "error", "tip", "max", "min", "justMonth", "monthDay"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: UicSliderComponent, selector: "ui-slider", inputs: ["icon", "iconColor", "label", "error", "tip", "min", "max", "step", "markerCount", "disabled", "loading"] }, { kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "component", type: UicPoolOptionsComponent, selector: "ui-pool-options", inputs: ["icon", "iconColor", "size", "label", "error", "tip", "disabled", "loading", "multy", "enabledListView", "listViewTitle", "options"] }, { kind: "component", type: UicTimePickerComponent, selector: "ui-time-picker", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "disabled", "label", "error", "tip", "interval"] }, { kind: "component", type: UicRadioComponent, selector: "ui-radio", inputs: ["icon", "iconColor", "label", "error", "tip", "disabled", "loading", "options", "emptyText", "direction"] }, { kind: "component", type: UicSearcherComponent, selector: "ui-searcher", inputs: ["icon", "iconColor", "internalIcon", "size", "label", "error", "tip", "showSubtitle", "disabled", "loading", "minimumSearchLength", "showSelected", "placeholder", "searchFn", "itemDisplayFn", "itemIsEnabledFn", "manualSearch", "stateless"], outputs: ["pickedItem"] }, { kind: "component", type: UicMultySelectComponent, selector: "ui-multy-select", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "showSubtitle", "disabled", "nonSelectedText", "allSelectedText", "loading", "options"] }, { kind: "component", type: UicFileInputComponent, selector: "ui-file-input", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "disabled", "loading", "multy", "fileTypes", "fileUidResolverFn"] }, { kind: "component", type: UicSelectComponent, selector: "ui-select", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "showSubtitle", "disabled", "nonSelectedText", "noneText", "emptyText", "searcherEnabled", "loading", "nullable", "options"] }, { kind: "directive", type: UicTextareaAutoresizeDirective, selector: "[textareaAutoresize]", inputs: ["minRows", "maxRows", "textareaAutoresize"] }, { kind: "directive", type: UicTextareaAutoresizeMinRowsDirective, selector: "[textareaAutoresizeMinRows]", inputs: ["textareaAutoresizeMinRows"] }, { kind: "directive", type: UicTextareaAutoresizeMaxRowsDirective, selector: "[textareaAutoresizeMaxRows]", inputs: ["textareaAutoresizeMaxRows"] }, { kind: "component", type: UicInputComponent, selector: "ui-input", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "disabled", "loading"], outputs: ["clickButton"] }, { kind: "component", type: UicCheckboxComponent, selector: "ui-checkbox", inputs: ["icon", "iconColor", "label", "tip", "type", "placeholder", "loading", "noPadding", "disabled"] }, { kind: "component", type: UicPhoneInputComponent, selector: "ui-phone-input", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "placeholder", "disabled", "loading", "preferredCountries", "flagPath", "countries"] }, { kind: "ngmodule", type: FormsModule }] });
4598
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicDynamicFormComponent, isStandalone: true, selector: "ui-dynamic-form", inputs: { fields: "fields", isSubForm: "isSubForm", form: "form", disabled: "disabled", voiceToTextSilenceMs: "voiceToTextSilenceMs", cols: "cols", fileUidResolverFn: "fileUidResolverFn", selectOptionsResolver: "selectOptionsResolver" }, ngImport: i0, template: "<div class=\"form\" [ngStyle]=\"{'--cols': cols, '--min': '180px'}\" [formGroup]=\"form\">\r\n @for (state of visibleFieldStates(); track state.field.name) {\r\n @let field = state.field;\r\n @let control = state.control;\r\n @let currentValue = state.value ?? '';\r\n @let error = getControlErrorMessages(control, field)[0];\r\n\r\n <div [style.grid-column]=\"getGridColumn(field)\">\r\n @if (['text', 'number'].includes(field.type)) {\r\n @let inputColor = getInputColor(field, currentValue);\r\n <ui-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [disabled]=\"!!control?.disabled\"\r\n [internalIconColor]=\"inputColor\"\r\n [internalIcon]=\"field.internalIcon || (currentValue !== '' ? getIconByColor(field, inputColor) : '')\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\">\r\n <input #inp\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [type]=\"field.type\"\r\n [step]=\"field.step\"\r\n [min]=\"field.min\"\r\n [max]=\"field.max\"\r\n [minLength]=\"field.minLength||null\"\r\n [maxlength]=\"field.maxLength||null\"\r\n [placeholder]=\"field.placeholder??field.label\"/>\r\n @if (field.maxLength && field.showCounter) {\r\n <span counter>{{inp.value.length}} /{{field.maxLength}}</span>\r\n }\r\n </ui-input>\r\n }\r\n @if (field.type === 'textarea') {\r\n <ui-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [disabled]=\"!!control?.disabled\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\">\r\n <textarea #ta\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [minLength]=\"field.minLength||null\"\r\n [maxlength]=\"field.maxLength||null\"\r\n [textareaAutoresize]=\"field.textareaResize\"\r\n [textareaAutoresizeMinRows]=\"field.resizeMinRows??1\"\r\n [textareaAutoresizeMaxRows]=\"field.resizeMaxRows??5\"\r\n [rows]=\"field.resizeMinRows??3\"\r\n [placeholder]=\"field.placeholder??field.label\"\r\n ></textarea>\r\n @if (field.voiceToTextEnabled && !control?.disabled) {\r\n @let listening = isListening(field.name);\r\n <div class=\"speak-icon\" (click)=\"ta.focus()\">\r\n <ui-button\r\n [disabled]=\"listening\"\r\n style=\"margin-bottom: 2px;\"\r\n (click)=\"voiceToText(ta, field.name)\"\r\n [icon]=\"listening?'ri-mic-fill':'ri-mic-line'\"\r\n [type]=\"listening?'filled':'bordered'\"\r\n size=\"s\"\r\n [iconOnly]=\"true\"\r\n ></ui-button>\r\n @if (listening) {\r\n <i class=\"ri-voiceprint-line\"></i>\r\n }\r\n </div>\r\n }\r\n @if (field.showCounter) {\r\n <span counter>\r\n {{ta.value.length}}\r\n @if (field.maxLength) {\r\n /{{field.maxLength}}\r\n }\r\n </span>\r\n }\r\n </ui-input>\r\n }\r\n @if (field.type === 'phone') {\r\n <ui-phone-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder||field.label||''\"\r\n >\r\n </ui-phone-input>\r\n }\r\n @else if (field.type === 'date') {\r\n <ui-date-picker\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [justMonth]=\"!!field.monthMode\"\r\n [min]=\"field.minDate??''\"\r\n [max]=\"field.maxDate??''\"\r\n [monthDay]=\"field.monthDay||'first'\"\r\n [formControlName]=\"field.name\"\r\n ></ui-date-picker>\r\n }\r\n @else if (field.type === 'time') {\r\n <ui-time-picker\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [interval]=\"field.timeInterval??5\"\r\n [formControlName]=\"field.name\"\r\n ></ui-time-picker>\r\n }\r\n @else if (field.type === 'select') {\r\n <ui-select\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [loading]=\"field.loading??false\"\r\n [nullable]=\"field.selectNullable??false\"\r\n [searcherEnabled]=\"field.selectSearchEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-select>\r\n }\r\n @else if (field.type === 'multyselect') {\r\n <ui-multy-select\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-multy-select>\r\n }\r\n @else if (field.type === 'searcher') {\r\n <ui-searcher\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\"\r\n [searchFn]=\"getSearcherFn(field)\"\r\n [itemDisplayFn]=\"getSearcherDisplayFn(field)\"\r\n [itemIsEnabledFn]=\"getSearcherIsEnabledFn(field)\"\r\n [placeholder]=\"field.placeholder??'common.search_ellipsis'\"\r\n [error]=\"error\"\r\n ></ui-searcher>\r\n }\r\n @else if (field.type === 'file') {\r\n <ui-file-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [multy]=\"field.multyEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [fileTypes]=\"field.fileTypes??null\"\r\n [fileUidResolverFn]=\"fileUidResolverFn\"\r\n [formControlName]=\"field.name\"\r\n ></ui-file-input>\r\n }\r\n @else if (['checkbox', 'switch'].includes(field.type)) {\r\n <ui-checkbox\r\n [label]=\"(field.label??'') + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [placeholder]=\"field.placeholder??''\"\r\n [tip]=\"field.tip??''\"\r\n [type]=\"field.type === 'checkbox' ? 'checkbox' : 'switch'\"\r\n ></ui-checkbox>\r\n }\r\n @else if (field.type === 'radio') {\r\n <ui-radio\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [tip]=\"field.tip??''\"\r\n [loading]=\"field.loading??false\"\r\n [error]=\"error\"\r\n [options]=\"field.options || []\"\r\n ></ui-radio>\r\n }\r\n @else if (field.type === 'slider') {\r\n <ui-slider\r\n [min]=\"field.min??0\"\r\n [max]=\"field.max??100\"\r\n [step]=\"field.sliderInterval??1\"\r\n [markerCount]=\"field.sliderMarks??5\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [tip]=\"field.tip??''\"\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n ></ui-slider>\r\n }\r\n @else if (field.type === 'pool') {\r\n <ui-pool-options\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [multy]=\"field.multyEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [listViewTitle]=\"field.poolTitle??'common.selected'\"\r\n [enabledListView]=\"field.poolEnabledListView||false\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-pool-options>\r\n }\r\n @else if (field.type === 'repeater') {\r\n <ui-repeater\r\n [formArray]=\"asFormArray(control)\"\r\n [field]=\"field\"\r\n [cols]=\"cols\"\r\n [error]=\"error\"\r\n ></ui-repeater>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".form{width:100%;display:grid;align-items:stretch;gap:1rem;padding-bottom:.75rem}@media (max-width: 479px){.form{grid-template-columns:1fr}}@media (min-width: 768px){.form{grid-template-columns:repeat(var(--cols),minmax(0,1fr))}}@media (min-width: 480px) and (max-width: 767px){.form{grid-template-columns:repeat(min(var(--cols),2),minmax(0,1fr))}}.form>*{min-width:0}h2{margin-top:1.5rem}.speak-icon{display:flex;width:100%;align-items:center;gap:10px}.speak-icon i{font-size:20px;color:var(--primary-500)}textarea{resize:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: UicDatePickerComponent, selector: "ui-date-picker", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "disabled", "label", "error", "tip", "max", "min", "justMonth", "monthDay"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: UicSliderComponent, selector: "ui-slider", inputs: ["icon", "iconColor", "label", "error", "tip", "min", "max", "step", "markerCount", "disabled", "loading"] }, { kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "component", type: UicPoolOptionsComponent, selector: "ui-pool-options", inputs: ["icon", "iconColor", "size", "label", "error", "tip", "disabled", "loading", "multy", "enabledListView", "listViewTitle", "options"] }, { kind: "component", type: UicTimePickerComponent, selector: "ui-time-picker", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "disabled", "label", "error", "tip", "interval"] }, { kind: "component", type: UicRadioComponent, selector: "ui-radio", inputs: ["icon", "iconColor", "label", "error", "tip", "disabled", "loading", "options", "emptyText", "direction"] }, { kind: "component", type: UicSearcherComponent, selector: "ui-searcher", inputs: ["icon", "iconColor", "internalIcon", "size", "label", "error", "tip", "showSubtitle", "disabled", "loading", "minimumSearchLength", "showSelected", "placeholder", "searchFn", "itemDisplayFn", "itemIsEnabledFn", "manualSearch", "stateless"], outputs: ["pickedItem"] }, { kind: "component", type: UicMultySelectComponent, selector: "ui-multy-select", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "showSubtitle", "disabled", "nonSelectedText", "allSelectedText", "loading", "options"] }, { kind: "component", type: UicFileInputComponent, selector: "ui-file-input", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "disabled", "loading", "multy", "fileTypes", "fileUidResolverFn"] }, { kind: "component", type: UicSelectComponent, selector: "ui-select", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "showSubtitle", "disabled", "nonSelectedText", "noneText", "emptyText", "searcherEnabled", "loading", "nullable", "options"] }, { kind: "directive", type: UicTextareaAutoresizeDirective, selector: "[textareaAutoresize]", inputs: ["minRows", "maxRows", "textareaAutoresize"] }, { kind: "directive", type: UicTextareaAutoresizeMinRowsDirective, selector: "[textareaAutoresizeMinRows]", inputs: ["textareaAutoresizeMinRows"] }, { kind: "directive", type: UicTextareaAutoresizeMaxRowsDirective, selector: "[textareaAutoresizeMaxRows]", inputs: ["textareaAutoresizeMaxRows"] }, { kind: "component", type: UicInputComponent, selector: "ui-input", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "disabled", "loading"], outputs: ["clickButton"] }, { kind: "component", type: UicCheckboxComponent, selector: "ui-checkbox", inputs: ["icon", "iconColor", "label", "tip", "type", "placeholder", "loading", "noPadding", "disabled"] }, { kind: "component", type: UicPhoneInputComponent, selector: "ui-phone-input", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "placeholder", "disabled", "loading", "preferredCountries", "flagPath", "countries"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: UicRepeaterComponent, selector: "ui-repeater", inputs: ["formArray", "field", "cols", "error"] }] });
4362
4599
  }
4363
4600
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicDynamicFormComponent, decorators: [{
4364
4601
  type: Component,
@@ -4381,10 +4618,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
4381
4618
  UicInputComponent,
4382
4619
  UicCheckboxComponent,
4383
4620
  UicPhoneInputComponent,
4384
- FormsModule
4385
- ], template: "<div class=\"form\" [ngStyle]=\"{'--cols': cols, '--min': '180px'}\" [formGroup]=\"form\">\r\n @for (state of visibleFieldStates(); track state.field.name) {\r\n @let field = state.field;\r\n @let control = state.control;\r\n @let currentValue = state.value ?? '';\r\n @let error = getControlErrorMessages(control, field)[0];\r\n\r\n <div [style.grid-column]=\"getGridColumn(field)\">\n @if (['text', 'number'].includes(field.type)) {\r\n @let inputColor = getInputColor(field, currentValue);\r\n <ui-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [disabled]=\"!!control?.disabled\"\r\n [internalIconColor]=\"inputColor\"\r\n [internalIcon]=\"field.internalIcon || (currentValue !== '' ? getIconByColor(field, inputColor) : '')\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\">\r\n <input #inp\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [type]=\"field.type\"\r\n [step]=\"field.step\"\r\n [min]=\"field.min\"\r\n [max]=\"field.max\"\r\n [minLength]=\"field.minLength||null\"\r\n [maxlength]=\"field.maxLength||null\"\r\n [placeholder]=\"field.placeholder??field.label\"/>\r\n @if (field.maxLength && field.showCounter) {\r\n <span counter>{{inp.value.length}} /{{field.maxLength}}</span>\r\n }\r\n </ui-input>\r\n }\r\n @if (field.type === 'textarea') {\r\n <ui-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [disabled]=\"!!control?.disabled\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\">\r\n <textarea #ta\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [minLength]=\"field.minLength||null\"\r\n [maxlength]=\"field.maxLength||null\"\r\n [textareaAutoresize]=\"field.textareaResize\"\r\n [textareaAutoresizeMinRows]=\"field.resizeMinRows??1\"\r\n [textareaAutoresizeMaxRows]=\"field.resizeMaxRows??5\"\r\n [rows]=\"field.resizeMinRows??3\"\r\n [placeholder]=\"field.placeholder??field.label\"\r\n ></textarea>\r\n @if (field.voiceToTextEnabled && !control?.disabled) {\r\n @let listening = isListening(field.name);\r\n <div class=\"speak-icon\" (click)=\"ta.focus()\">\r\n <ui-button\r\n [disabled]=\"listening\"\r\n style=\"margin-bottom: 2px;\"\r\n (click)=\"voiceToText(ta, field.name)\"\r\n [icon]=\"listening?'ri-mic-fill':'ri-mic-line'\"\r\n [type]=\"listening?'filled':'bordered'\"\r\n size=\"s\"\r\n [iconOnly]=\"true\"\r\n ></ui-button>\r\n @if (listening) {\r\n <i class=\"ri-voiceprint-line\"></i>\r\n }\r\n </div>\r\n }\r\n @if (field.showCounter) {\r\n <span counter>\r\n {{ta.value.length}}\r\n @if (field.maxLength) {\r\n /{{field.maxLength}}\r\n }\r\n </span>\r\n }\r\n </ui-input>\r\n }\r\n @if (field.type === 'phone') {\r\n <ui-phone-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder||field.label||''\"\r\n >\r\n </ui-phone-input>\r\n }\r\n @else if (field.type === 'date') {\r\n <ui-date-picker\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [justMonth]=\"!!field.monthMode\"\r\n [min]=\"field.minDate??''\"\r\n [max]=\"field.maxDate??''\"\r\n [monthDay]=\"field.monthDay||'first'\"\r\n [formControlName]=\"field.name\"\r\n ></ui-date-picker>\r\n }\r\n @else if (field.type === 'time') {\r\n <ui-time-picker\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [interval]=\"field.timeInterval??5\"\r\n [formControlName]=\"field.name\"\r\n ></ui-time-picker>\r\n }\r\n @else if (field.type === 'select') {\r\n <ui-select\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [loading]=\"field.loading??false\"\r\n [nullable]=\"field.selectNullable??false\"\r\n [searcherEnabled]=\"field.selectSearchEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-select>\r\n }\r\n @else if (field.type === 'multyselect') {\r\n <ui-multy-select\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-multy-select>\r\n }\r\n @else if (field.type === 'searcher') {\r\n <ui-searcher\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\"\r\n [searchFn]=\"getSearcherFn(field)\"\r\n [itemDisplayFn]=\"getSearcherDisplayFn(field)\"\r\n [itemIsEnabledFn]=\"getSearcherIsEnabledFn(field)\"\r\n [placeholder]=\"field.placeholder??'common.search_ellipsis'\"\r\n [error]=\"error\"\r\n ></ui-searcher>\r\n }\r\n @else if (field.type === 'file') {\r\n <ui-file-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [multy]=\"field.multyEnabled??false\"\n [tip]=\"field.tip??''\"\n [fileTypes]=\"field.fileTypes??null\"\n [fileUidResolverFn]=\"fileUidResolverFn\"\n [formControlName]=\"field.name\"\n ></ui-file-input>\n }\n @else if (['checkbox', 'switch'].includes(field.type)) {\r\n <ui-checkbox\r\n [label]=\"(field.label??'') + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [placeholder]=\"field.placeholder??''\"\r\n [tip]=\"field.tip??''\"\r\n [type]=\"field.type === 'checkbox' ? 'checkbox' : 'switch'\"\r\n ></ui-checkbox>\r\n }\r\n @else if (field.type === 'radio') {\r\n <ui-radio\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [tip]=\"field.tip??''\"\r\n [loading]=\"field.loading??false\"\r\n [error]=\"error\"\r\n [options]=\"field.options || []\"\r\n ></ui-radio>\r\n }\r\n @else if (field.type === 'slider') {\r\n <ui-slider\r\n [min]=\"field.min??0\"\r\n [max]=\"field.max??100\"\r\n [step]=\"field.sliderInterval??1\"\r\n [markerCount]=\"field.sliderMarks??5\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [tip]=\"field.tip??''\"\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n ></ui-slider>\r\n }\r\n @else if (field.type === 'pool') {\r\n <ui-pool-options\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [multy]=\"field.multyEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [listViewTitle]=\"field.poolTitle??'common.selected'\"\r\n [enabledListView]=\"field.poolEnabledListView||false\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-pool-options>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".form{width:100%;display:grid;align-items:stretch;gap:1rem;padding-bottom:.75rem}@media (max-width: 479px){.form{grid-template-columns:1fr}}@media (min-width: 768px){.form{grid-template-columns:repeat(var(--cols),minmax(0,1fr))}}@media (min-width: 480px) and (max-width: 767px){.form{grid-template-columns:repeat(min(var(--cols),2),minmax(0,1fr))}}.form>*{min-width:0}h2{margin-top:1.5rem}.speak-icon{display:flex;width:100%;align-items:center;gap:10px}.speak-icon i{font-size:20px;color:var(--primary-500)}textarea{resize:none}\n"] }]
4621
+ FormsModule,
4622
+ UicRepeaterComponent
4623
+ ], template: "<div class=\"form\" [ngStyle]=\"{'--cols': cols, '--min': '180px'}\" [formGroup]=\"form\">\r\n @for (state of visibleFieldStates(); track state.field.name) {\r\n @let field = state.field;\r\n @let control = state.control;\r\n @let currentValue = state.value ?? '';\r\n @let error = getControlErrorMessages(control, field)[0];\r\n\r\n <div [style.grid-column]=\"getGridColumn(field)\">\r\n @if (['text', 'number'].includes(field.type)) {\r\n @let inputColor = getInputColor(field, currentValue);\r\n <ui-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [disabled]=\"!!control?.disabled\"\r\n [internalIconColor]=\"inputColor\"\r\n [internalIcon]=\"field.internalIcon || (currentValue !== '' ? getIconByColor(field, inputColor) : '')\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\">\r\n <input #inp\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [type]=\"field.type\"\r\n [step]=\"field.step\"\r\n [min]=\"field.min\"\r\n [max]=\"field.max\"\r\n [minLength]=\"field.minLength||null\"\r\n [maxlength]=\"field.maxLength||null\"\r\n [placeholder]=\"field.placeholder??field.label\"/>\r\n @if (field.maxLength && field.showCounter) {\r\n <span counter>{{inp.value.length}} /{{field.maxLength}}</span>\r\n }\r\n </ui-input>\r\n }\r\n @if (field.type === 'textarea') {\r\n <ui-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [disabled]=\"!!control?.disabled\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\">\r\n <textarea #ta\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [minLength]=\"field.minLength||null\"\r\n [maxlength]=\"field.maxLength||null\"\r\n [textareaAutoresize]=\"field.textareaResize\"\r\n [textareaAutoresizeMinRows]=\"field.resizeMinRows??1\"\r\n [textareaAutoresizeMaxRows]=\"field.resizeMaxRows??5\"\r\n [rows]=\"field.resizeMinRows??3\"\r\n [placeholder]=\"field.placeholder??field.label\"\r\n ></textarea>\r\n @if (field.voiceToTextEnabled && !control?.disabled) {\r\n @let listening = isListening(field.name);\r\n <div class=\"speak-icon\" (click)=\"ta.focus()\">\r\n <ui-button\r\n [disabled]=\"listening\"\r\n style=\"margin-bottom: 2px;\"\r\n (click)=\"voiceToText(ta, field.name)\"\r\n [icon]=\"listening?'ri-mic-fill':'ri-mic-line'\"\r\n [type]=\"listening?'filled':'bordered'\"\r\n size=\"s\"\r\n [iconOnly]=\"true\"\r\n ></ui-button>\r\n @if (listening) {\r\n <i class=\"ri-voiceprint-line\"></i>\r\n }\r\n </div>\r\n }\r\n @if (field.showCounter) {\r\n <span counter>\r\n {{ta.value.length}}\r\n @if (field.maxLength) {\r\n /{{field.maxLength}}\r\n }\r\n </span>\r\n }\r\n </ui-input>\r\n }\r\n @if (field.type === 'phone') {\r\n <ui-phone-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder||field.label||''\"\r\n >\r\n </ui-phone-input>\r\n }\r\n @else if (field.type === 'date') {\r\n <ui-date-picker\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [justMonth]=\"!!field.monthMode\"\r\n [min]=\"field.minDate??''\"\r\n [max]=\"field.maxDate??''\"\r\n [monthDay]=\"field.monthDay||'first'\"\r\n [formControlName]=\"field.name\"\r\n ></ui-date-picker>\r\n }\r\n @else if (field.type === 'time') {\r\n <ui-time-picker\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [interval]=\"field.timeInterval??5\"\r\n [formControlName]=\"field.name\"\r\n ></ui-time-picker>\r\n }\r\n @else if (field.type === 'select') {\r\n <ui-select\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [loading]=\"field.loading??false\"\r\n [nullable]=\"field.selectNullable??false\"\r\n [searcherEnabled]=\"field.selectSearchEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-select>\r\n }\r\n @else if (field.type === 'multyselect') {\r\n <ui-multy-select\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [tip]=\"field.tip??''\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-multy-select>\r\n }\r\n @else if (field.type === 'searcher') {\r\n <ui-searcher\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [tip]=\"field.tip??''\"\r\n [searchFn]=\"getSearcherFn(field)\"\r\n [itemDisplayFn]=\"getSearcherDisplayFn(field)\"\r\n [itemIsEnabledFn]=\"getSearcherIsEnabledFn(field)\"\r\n [placeholder]=\"field.placeholder??'common.search_ellipsis'\"\r\n [error]=\"error\"\r\n ></ui-searcher>\r\n }\r\n @else if (field.type === 'file') {\r\n <ui-file-input\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [internalIcon]=\"field.internalIcon??''\"\r\n [internalIconColor]=\"field.internalIconColor??'grey'\"\r\n [multy]=\"field.multyEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [fileTypes]=\"field.fileTypes??null\"\r\n [fileUidResolverFn]=\"fileUidResolverFn\"\r\n [formControlName]=\"field.name\"\r\n ></ui-file-input>\r\n }\r\n @else if (['checkbox', 'switch'].includes(field.type)) {\r\n <ui-checkbox\r\n [label]=\"(field.label??'') + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [placeholder]=\"field.placeholder??''\"\r\n [tip]=\"field.tip??''\"\r\n [type]=\"field.type === 'checkbox' ? 'checkbox' : 'switch'\"\r\n ></ui-checkbox>\r\n }\r\n @else if (field.type === 'radio') {\r\n <ui-radio\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [tip]=\"field.tip??''\"\r\n [loading]=\"field.loading??false\"\r\n [error]=\"error\"\r\n [options]=\"field.options || []\"\r\n ></ui-radio>\r\n }\r\n @else if (field.type === 'slider') {\r\n <ui-slider\r\n [min]=\"field.min??0\"\r\n [max]=\"field.max??100\"\r\n [step]=\"field.sliderInterval??1\"\r\n [markerCount]=\"field.sliderMarks??5\"\r\n [id]=\"field.name\"\r\n [formControlName]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [tip]=\"field.tip??''\"\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n ></ui-slider>\r\n }\r\n @else if (field.type === 'pool') {\r\n <ui-pool-options\r\n [error]=\"error\"\r\n [label]=\"field.label + (field.required ? ' *' : '')\"\r\n [id]=\"field.name\"\r\n [icon]=\"field.icon??''\"\r\n [loading]=\"field.loading??false\"\r\n [multy]=\"field.multyEnabled??false\"\r\n [tip]=\"field.tip??''\"\r\n [listViewTitle]=\"field.poolTitle??'common.selected'\"\r\n [enabledListView]=\"field.poolEnabledListView||false\"\r\n [formControlName]=\"field.name\"\r\n [options]=\"field.options || []\"\r\n ></ui-pool-options>\r\n }\r\n @else if (field.type === 'repeater') {\r\n <ui-repeater\r\n [formArray]=\"asFormArray(control)\"\r\n [field]=\"field\"\r\n [cols]=\"cols\"\r\n [error]=\"error\"\r\n ></ui-repeater>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".form{width:100%;display:grid;align-items:stretch;gap:1rem;padding-bottom:.75rem}@media (max-width: 479px){.form{grid-template-columns:1fr}}@media (min-width: 768px){.form{grid-template-columns:repeat(var(--cols),minmax(0,1fr))}}@media (min-width: 480px) and (max-width: 767px){.form{grid-template-columns:repeat(min(var(--cols),2),minmax(0,1fr))}}.form>*{min-width:0}h2{margin-top:1.5rem}.speak-icon{display:flex;width:100%;align-items:center;gap:10px}.speak-icon i{font-size:20px;color:var(--primary-500)}textarea{resize:none}\n"] }]
4386
4624
  }], ctorParameters: () => [{ type: i0.NgZone }], propDecorators: { fields: [{
4387
4625
  type: Input
4626
+ }], isSubForm: [{
4627
+ type: Input
4388
4628
  }], form: [{
4389
4629
  type: Input,
4390
4630
  args: [{ required: true }]
@@ -4472,10 +4712,12 @@ class UicFormWrapperComponent {
4472
4712
  buildForm() {
4473
4713
  this.formValueSub?.unsubscribe();
4474
4714
  this.formValueSub = null;
4475
- const fields = this.collectAllFields();
4476
- const numericFields = fields.filter(field => field.type === 'number');
4715
+ const allFields = this.collectAllFields();
4716
+ const regularFields = allFields.filter(field => field.type !== 'repeater');
4717
+ const repeaterFields = allFields.filter(field => field.type === 'repeater');
4718
+ const numericFields = regularFields.filter(field => field.type === 'number');
4477
4719
  const newForm = this.fb.group({});
4478
- fields.forEach(field => {
4720
+ regularFields.forEach(field => {
4479
4721
  const validators = this.mapValidatorsFromField(field);
4480
4722
  const initial = this.normalizeFieldValue(field, this.newFieldInitialValue(field, true));
4481
4723
  const controlState = {
@@ -4484,6 +4726,25 @@ class UicFormWrapperComponent {
4484
4726
  };
4485
4727
  newForm.addControl(field.name, this.fb.control(controlState, validators));
4486
4728
  });
4729
+ repeaterFields.forEach(field => {
4730
+ const arrayValidators = field.required
4731
+ ? [this.repeaterArrayValidator(field.repeaterMinItems ?? 1)]
4732
+ : [];
4733
+ const formArray = new FormArray([], arrayValidators);
4734
+ const initialItems = this.initialValues?.[field.name];
4735
+ if (Array.isArray(initialItems) && initialItems.length > 0) {
4736
+ initialItems.forEach(itemValues => {
4737
+ formArray.push(this.buildRepeaterGroup(field, itemValues), { emitEvent: false });
4738
+ });
4739
+ }
4740
+ else {
4741
+ const min = field.repeaterMinItems ?? 0;
4742
+ for (let i = 0; i < min; i++) {
4743
+ formArray.push(this.buildRepeaterGroup(field, {}), { emitEvent: false });
4744
+ }
4745
+ }
4746
+ newForm.addControl(field.name, formArray);
4747
+ });
4487
4748
  this.formValueSub = newForm.valueChanges.subscribe(() => {
4488
4749
  this.normalizeNumericControls(newForm, numericFields);
4489
4750
  this.handleFormChange();
@@ -4494,6 +4755,21 @@ class UicFormWrapperComponent {
4494
4755
  this.updateDisabledState();
4495
4756
  this.focusConfiguredField();
4496
4757
  }
4758
+ buildRepeaterGroup(field, values = {}) {
4759
+ const group = this.fb.group({});
4760
+ (field.repeaterFields ?? []).forEach(subField => {
4761
+ const validators = this.mapValidatorsFromField(subField);
4762
+ const value = values[subField.name] ?? this.blankValue(subField);
4763
+ group.addControl(subField.name, this.fb.control({ value, disabled: this.disabled || !!subField.disabled }, validators));
4764
+ });
4765
+ return group;
4766
+ }
4767
+ repeaterArrayValidator(min) {
4768
+ return (control) => {
4769
+ const arr = control;
4770
+ return arr.length >= min ? null : { minLengthArray: { required: min, actual: arr.length } };
4771
+ };
4772
+ }
4497
4773
  focusConfiguredField() {
4498
4774
  if (!this.focusFieldName || this.hasFocusedCurrentTrigger)
4499
4775
  return;
@@ -4536,6 +4812,20 @@ class UicFormWrapperComponent {
4536
4812
  }
4537
4813
  for (const block of this.effectiveSchema.blocks) {
4538
4814
  block.fields = this.updateFieldOptions(block);
4815
+ block.fields = block.fields.map(field => {
4816
+ if (field.type !== 'repeater' || !field.repeaterFields?.length)
4817
+ return field;
4818
+ const namespace = this.externalData[field.name];
4819
+ if (!namespace || typeof namespace !== 'object' || Array.isArray(namespace))
4820
+ return field;
4821
+ const updatedRepeaterFields = field.repeaterFields.map(subField => {
4822
+ if (subField.type !== 'select' && subField.type !== 'pool' && subField.type !== 'multyselect')
4823
+ return subField;
4824
+ const subOptions = namespace[subField.name];
4825
+ return Array.isArray(subOptions) ? { ...subField, options: subOptions } : subField;
4826
+ });
4827
+ return { ...field, repeaterFields: updatedRepeaterFields };
4828
+ });
4539
4829
  }
4540
4830
  }
4541
4831
  mapValidatorsFromField(field) {
@@ -4583,6 +4873,16 @@ class UicFormWrapperComponent {
4583
4873
  updateOptionsSources(force = false) {
4584
4874
  const fields = this.collectAllFields().filter(field => this.hasUsableOptionsSource(field));
4585
4875
  const fieldNames = new Set(fields.map(field => field.name));
4876
+ for (const block of this.effectiveSchema?.blocks ?? []) {
4877
+ for (const f of block.fields) {
4878
+ if (f.type === 'repeater' && f.repeaterFields?.length) {
4879
+ f.repeaterFields.forEach(sf => {
4880
+ if (this.hasUsableOptionsSource(sf))
4881
+ fieldNames.add(`${f.name}__${sf.name}`);
4882
+ });
4883
+ }
4884
+ }
4885
+ }
4586
4886
  for (const fieldName of this.optionSourceSubs.keys()) {
4587
4887
  if (!fieldNames.has(fieldName)) {
4588
4888
  this.optionSourceSubs.get(fieldName)?.unsubscribe();
@@ -4591,6 +4891,80 @@ class UicFormWrapperComponent {
4591
4891
  }
4592
4892
  }
4593
4893
  fields.forEach(field => this.resolveOptionsSourceField(field, force));
4894
+ this.updateRepeaterSubFieldOptionsSources(force);
4895
+ }
4896
+ updateRepeaterSubFieldOptionsSources(force = false) {
4897
+ if (!this.effectiveSchema?.blocks?.length)
4898
+ return;
4899
+ for (const block of this.effectiveSchema.blocks) {
4900
+ for (const field of block.fields) {
4901
+ if (field.type !== 'repeater' || !field.repeaterFields?.length)
4902
+ continue;
4903
+ field.repeaterFields.forEach(subField => {
4904
+ if (this.hasUsableOptionsSource(subField)) {
4905
+ this.resolveRepeaterSubFieldOptionsSource(field, subField, force);
4906
+ }
4907
+ });
4908
+ }
4909
+ }
4910
+ }
4911
+ resolveRepeaterSubFieldOptionsSource(parentField, subField, force = false) {
4912
+ const source = subField.optionsSource;
4913
+ const subFieldKey = `${parentField.name}__${subField.name}`;
4914
+ if (!this.selectOptionsResolver) {
4915
+ this.updateRepeaterSubFieldOptionsState(parentField.name, subField.name, {
4916
+ options: subField.options ?? [],
4917
+ loading: false
4918
+ });
4919
+ return;
4920
+ }
4921
+ if (!force && this.optionSourceSubs.has(subFieldKey))
4922
+ return;
4923
+ this.optionSourceSubs.get(subFieldKey)?.unsubscribe();
4924
+ this.updateRepeaterSubFieldOptionsState(parentField.name, subField.name, { loading: true });
4925
+ const values = this.toNestedValues(this.form?.getRawValue?.() ?? {});
4926
+ let optionsResult;
4927
+ try {
4928
+ optionsResult = this.selectOptionsResolver(source, { field: subField, values, dependencyValue: undefined });
4929
+ }
4930
+ catch (error) {
4931
+ this.handleOptionsSourceError(subField, source, error);
4932
+ return;
4933
+ }
4934
+ const sub = this.toObservable(optionsResult).subscribe({
4935
+ next: items => {
4936
+ const options = this.mapOptionsSourceItems(items, source);
4937
+ this.updateRepeaterSubFieldOptionsState(parentField.name, subField.name, { options, loading: false });
4938
+ },
4939
+ error: (error) => this.handleOptionsSourceError(subField, source, error),
4940
+ complete: () => this.updateRepeaterSubFieldOptionsState(parentField.name, subField.name, { loading: false })
4941
+ });
4942
+ this.optionSourceSubs.set(subFieldKey, sub);
4943
+ }
4944
+ updateRepeaterSubFieldOptionsState(repeaterFieldName, subFieldName, values) {
4945
+ if (!this.effectiveSchema?.blocks?.length)
4946
+ return;
4947
+ for (const block of this.effectiveSchema.blocks) {
4948
+ const repeaterIdx = block.fields.findIndex(f => f.name === repeaterFieldName);
4949
+ if (repeaterIdx === -1)
4950
+ continue;
4951
+ const repeaterField = block.fields[repeaterIdx];
4952
+ if (!repeaterField.repeaterFields)
4953
+ continue;
4954
+ const subIdx = repeaterField.repeaterFields.findIndex(sf => sf.name === subFieldName);
4955
+ if (subIdx === -1)
4956
+ continue;
4957
+ const updatedRepeaterFields = [...repeaterField.repeaterFields];
4958
+ updatedRepeaterFields[subIdx] = {
4959
+ ...updatedRepeaterFields[subIdx],
4960
+ ...(values.options !== undefined ? { options: values.options } : {}),
4961
+ ...(values.loading !== undefined ? { loading: values.loading } : {})
4962
+ };
4963
+ block.fields[repeaterIdx] = { ...repeaterField, repeaterFields: updatedRepeaterFields };
4964
+ block.fields = [...block.fields];
4965
+ this.effectiveSchema.blocks = [...this.effectiveSchema.blocks];
4966
+ return;
4967
+ }
4594
4968
  }
4595
4969
  updateDependentOptionsSources() {
4596
4970
  const fields = this.collectAllFields().filter(field => {
@@ -4834,12 +5208,28 @@ class UicFormWrapperComponent {
4834
5208
  return;
4835
5209
  const fields = this.collectAllFields();
4836
5210
  const fieldMap = new Map(fields.map(field => [field.name, field]));
4837
- const resetValues = {};
4838
5211
  Object.keys(this.form.controls).forEach(name => {
4839
5212
  const field = fieldMap.get(name);
4840
- resetValues[name] = this.resolveResetValue(name, field, hardReset);
5213
+ const control = this.form.get([name]);
5214
+ if (!control)
5215
+ return;
5216
+ if (field?.type === 'repeater') {
5217
+ const formArray = control;
5218
+ while (formArray.length > 0) {
5219
+ formArray.removeAt(0, { emitEvent: false });
5220
+ }
5221
+ const min = field.repeaterMinItems ?? 0;
5222
+ for (let i = 0; i < min; i++) {
5223
+ formArray.push(this.buildRepeaterGroup(field, {}), { emitEvent: false });
5224
+ }
5225
+ formArray.markAsPristine();
5226
+ formArray.markAsUntouched();
5227
+ }
5228
+ else {
5229
+ const resetValue = this.resolveResetValue(name, field, hardReset);
5230
+ control.reset(resetValue, { emitEvent: false });
5231
+ }
4841
5232
  });
4842
- this.form.reset(resetValues, { emitEvent: false });
4843
5233
  this.handleFormChange();
4844
5234
  }
4845
5235
  addFieldControl(field, after) {
@@ -5030,7 +5420,7 @@ class UicFormWrapperComponent {
5030
5420
  };
5031
5421
  }
5032
5422
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicFormWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5033
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicFormWrapperComponent, isStandalone: true, selector: "ui-form-wrapper", inputs: { schema: "schema", fields: "fields", cols: "cols", externalData: "externalData", selectOptionsResolver: "selectOptionsResolver", loading: "loading", disabled: "disabled", showButtons: "showButtons", fillSelects: "fillSelects", initialValues: "initialValues", focusFieldName: "focusFieldName", focusFieldTrigger: "focusFieldTrigger", fileUidResolverFn: "fileUidResolverFn" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", optionsSourceError: "optionsSourceError" }, providers: [UicFormStateService], usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"form\" (ngSubmit)=\"handleSubmit()\">\r\n @for (block of effectiveSchema.blocks; track $index) {\r\n \r\n <section class=\"form-block\">\r\n @if (block.title){\r\n <div class=\"block-title\">{{ block.title }}</div>\r\n }\r\n @if (block.subtitle){\r\n <div class=\"block-subtitle\">{{ block.subtitle }}</div>\r\n }\r\n @if (loading) {\r\n <ui-skeleton-loader\r\n [cols]=\"effectiveSchema.cols\"\r\n [inputs]=\"block.fields.length\"\r\n ></ui-skeleton-loader>\r\n } @else {\r\n <ui-dynamic-form\n [cols]=\"effectiveSchema.cols\"\n [disabled]=\"disabled\"\n [fileUidResolverFn]=\"fileUidResolverFn\"\n [fields]=\"block.fields\"\n [form]=\"form\"\n [selectOptionsResolver]=\"selectOptionsResolver\">\n </ui-dynamic-form>\n }\r\n </section>\r\n }\r\n @if ( showButtons ) {\r\n\r\n <div class=\"form-buttons\">\r\n <ui-button color=\"black\" type=\"bordered\" (click)=\"clean()\">{{'common.clear' | uicTranslate}}</ui-button>\r\n <ui-button color=\"black\" (click)=\"submit()\">{{'common.save' | uicTranslate}}</ui-button>\r\n </div>\r\n }\r\n</form>\r\n", styles: [":host{display:block;padding:.25rem 0}.block-title{font-size:1.625rem;font-weight:600;line-height:calc(1.625rem + 4px);margin:1rem 0;color:var(--grey-950)}.block-subtitle{font-size:1.25rem;font-weight:600;line-height:calc(1.25rem + 4px);margin:.75rem 0;color:var(--grey-950)}.form-buttons{display:flex;gap:10px;width:100%}\n"], dependencies: [{ kind: "component", type: UicDynamicFormComponent, selector: "ui-dynamic-form", inputs: ["fields", "form", "disabled", "voiceToTextSilenceMs", "cols", "fileUidResolverFn", "selectOptionsResolver"] }, { kind: "component", type: UicSkeletonLoaderComponent, selector: "ui-skeleton-loader", inputs: ["inputs", "cols"] }, { kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
5423
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicFormWrapperComponent, isStandalone: true, selector: "ui-form-wrapper", inputs: { schema: "schema", fields: "fields", cols: "cols", externalData: "externalData", selectOptionsResolver: "selectOptionsResolver", loading: "loading", disabled: "disabled", showButtons: "showButtons", fillSelects: "fillSelects", initialValues: "initialValues", focusFieldName: "focusFieldName", focusFieldTrigger: "focusFieldTrigger", fileUidResolverFn: "fileUidResolverFn" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", optionsSourceError: "optionsSourceError" }, providers: [UicFormStateService], usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"form\" (ngSubmit)=\"handleSubmit()\">\r\n @for (block of effectiveSchema.blocks; track $index) {\r\n \r\n <section class=\"form-block\">\r\n @if (block.title){\r\n <div class=\"block-title\">{{ block.title }}</div>\r\n }\r\n @if (block.subtitle){\r\n <div class=\"block-subtitle\">{{ block.subtitle }}</div>\r\n }\r\n @if (loading) {\r\n <ui-skeleton-loader\r\n [cols]=\"effectiveSchema.cols\"\r\n [inputs]=\"block.fields.length\"\r\n ></ui-skeleton-loader>\r\n } @else {\r\n <ui-dynamic-form\n [cols]=\"effectiveSchema.cols\"\n [disabled]=\"disabled\"\n [fileUidResolverFn]=\"fileUidResolverFn\"\n [fields]=\"block.fields\"\n [form]=\"form\"\n [selectOptionsResolver]=\"selectOptionsResolver\">\n </ui-dynamic-form>\n }\r\n </section>\r\n }\r\n @if ( showButtons ) {\r\n\r\n <div class=\"form-buttons\">\r\n <ui-button color=\"black\" type=\"bordered\" (click)=\"clean()\">{{'common.clear' | uicTranslate}}</ui-button>\r\n <ui-button color=\"black\" (click)=\"submit()\">{{'common.save' | uicTranslate}}</ui-button>\r\n </div>\r\n }\r\n</form>\r\n", styles: [":host{display:block;padding:.25rem 0}.block-title{font-size:1.625rem;font-weight:600;line-height:calc(1.625rem + 4px);margin:1rem 0;color:var(--grey-950)}.block-subtitle{font-size:1.25rem;font-weight:600;line-height:calc(1.25rem + 4px);margin:.75rem 0;color:var(--grey-950)}.form-buttons{display:flex;gap:10px;width:100%}\n"], dependencies: [{ kind: "component", type: UicDynamicFormComponent, selector: "ui-dynamic-form", inputs: ["fields", "isSubForm", "form", "disabled", "voiceToTextSilenceMs", "cols", "fileUidResolverFn", "selectOptionsResolver"] }, { kind: "component", type: UicSkeletonLoaderComponent, selector: "ui-skeleton-loader", inputs: ["inputs", "cols"] }, { kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
5034
5424
  }
5035
5425
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicFormWrapperComponent, decorators: [{
5036
5426
  type: Component,
@@ -6408,7 +6798,10 @@ function mapEditableBlocksToBlocks(blocks, editable) {
6408
6798
  ...field.fieldData,
6409
6799
  disabled: !editable,
6410
6800
  optionsSource,
6411
- visibilityRules: visibilityRules ?? field.fieldData.visibilityRules
6801
+ visibilityRules: visibilityRules ?? field.fieldData.visibilityRules,
6802
+ ...(field.repeaterSubFields?.length
6803
+ ? { repeaterFields: field.repeaterSubFields.map(sf => ({ ...sf.fieldData, disabled: !editable })) }
6804
+ : {})
6412
6805
  };
6413
6806
  })
6414
6807
  }));
@@ -8472,22 +8865,33 @@ const simpleFade = trigger('simpleFade', [
8472
8865
  ]);
8473
8866
 
8474
8867
  ;
8868
+ const VISIBILITY_OPERATOR_OPTIONS = [
8869
+ { id: 'equals', text: 'equals' },
8870
+ { id: 'notEquals', text: 'notEquals' },
8871
+ { id: 'greaterThan', text: 'greaterThan' },
8872
+ { id: 'lessThan', text: 'lessThan' },
8873
+ { id: 'includes', text: 'includes' },
8874
+ { id: 'notIncludes', text: 'notIncludes' },
8875
+ { id: 'isEmpty', text: 'isEmpty' },
8876
+ { id: 'isNotEmpty', text: 'isNotEmpty' }
8877
+ ];
8475
8878
  const FIELDS_CONFIG = [
8476
- { value: 'text', icon: 'ri-text', detail: 'Campo de texto en una sola linea', label: 'Texto corto', properties: ['placeholder', 'minLength', 'maxLength', 'showCounter'] },
8477
- { value: 'textarea', icon: 'ri-file-text-line', detail: 'Texto multilinea con ajuste de altura', label: 'Area de texto', properties: ['placeholder', 'minLength', 'maxLength', 'showCounter', 'voiceToTextEnabled', 'textareaResize', 'resizeMinRows', 'resizeMaxRows'] },
8478
- { value: 'number', icon: 'ri-hashtag', detail: 'Campo numerico con control de paso', label: 'Numero', properties: ['placeholder', 'step', 'min', 'max'] },
8479
- { value: 'phone', icon: 'ri-phone-line', detail: 'Telefono con pais y codigo', label: 'Telefono', properties: ['placeholder'] },
8480
- { value: 'select', icon: 'ri-list-unordered', detail: 'Seleccion simple desde opciones', label: 'Selector', properties: ['options', 'selectSearchEnabled', 'selectNullable'] },
8481
- { value: 'multyselect', icon: 'ri-list-check-2', detail: 'Seleccion multiple desde opciones', label: 'Selector multiple', properties: ['options'] },
8482
- { value: 'date', icon: 'ri-calendar-2-line', detail: 'Selector de fecha o mes', label: 'Fecha', properties: ['monthMode', 'monthDay', 'minDate', 'maxDate'] },
8483
- { value: 'time', icon: 'ri-timer-2-line', detail: 'Selector de hora', label: 'Hora', properties: ['timeInterval'] },
8484
- { value: 'radio', icon: 'ri-radio-button-line', detail: 'Seleccion unica entre opciones', label: 'Radio', properties: ['options'] },
8485
- { value: 'checkbox', icon: 'ri-checkbox-line', detail: 'Campo booleano tipo check', label: 'Checkbox', properties: ['placeholder'] },
8486
- { value: 'switch', icon: 'ri-toggle-line', detail: 'Campo booleano tipo switch', label: 'Switch', properties: ['placeholder'] },
8487
- { value: 'pool', icon: 'ri-list-indefinite', detail: 'Seleccion con lista y detalle por item', label: 'Multiopciones', properties: ['options', 'multyEnabled', 'poolEnabledListView', 'poolTitle'] },
8488
- { value: 'file', icon: 'ri-attachment-line', detail: 'Carga de archivos', label: 'Archivo', properties: ['multyEnabled', 'fileTypes', 'iaValidation', 'iaValidationPrompt'] },
8489
- { value: 'searcher', icon: 'ri-seo-line', detail: 'Buscador con fuente de datos externa', label: 'Buscador', properties: ['placeholder', 'optionsSource.key', 'optionsSource.idField', 'optionsSource.textField', 'optionsSource.textTemplate', 'optionsSource.dependsOn', 'optionsSource.paramName'] },
8490
- { value: 'slider', icon: 'ri-equalizer-2-line', detail: 'Control deslizante continuo', label: 'Deslizador', properties: ['min', 'max', 'sliderInterval', 'sliderMarks'] }
8879
+ { value: 'text', icon: 'ri-text', detail: 'Campo de texto en una sola linea', label: 'Texto corto', properties: ['placeholder', 'minLength', 'maxLength', 'showCounter'], visibility: { outputType: 'string', operators: ['equals', 'notEquals', 'includes', 'notIncludes', 'isEmpty', 'isNotEmpty'] } },
8880
+ { value: 'textarea', icon: 'ri-file-text-line', detail: 'Texto multilinea con ajuste de altura', label: 'Area de texto', properties: ['placeholder', 'minLength', 'maxLength', 'showCounter', 'voiceToTextEnabled', 'textareaResize', 'resizeMinRows', 'resizeMaxRows'], visibility: { outputType: 'string', operators: ['equals', 'notEquals', 'includes', 'notIncludes', 'isEmpty', 'isNotEmpty'] } },
8881
+ { value: 'number', icon: 'ri-hashtag', detail: 'Campo numerico con control de paso', label: 'Numero', properties: ['placeholder', 'step', 'min', 'max'], visibility: { outputType: 'number', operators: ['equals', 'notEquals', 'greaterThan', 'lessThan', 'isEmpty', 'isNotEmpty'] } },
8882
+ { value: 'phone', icon: 'ri-phone-line', detail: 'Telefono con pais y codigo', label: 'Telefono', properties: ['placeholder'], visibility: { outputType: 'UicPhoneInputValue', operators: ['includes', 'notIncludes', 'isEmpty', 'isNotEmpty'] } },
8883
+ { value: 'select', icon: 'ri-arrow-drop-down-line', detail: 'Seleccion simple desde opciones', label: 'Selector', properties: ['options', 'selectSearchEnabled', 'selectNullable'], visibility: { outputType: 'string', operators: ['equals', 'notEquals', 'includes', 'notIncludes', 'isEmpty', 'isNotEmpty'] } },
8884
+ { value: 'multyselect', icon: 'ri-arrow-down-double-line', detail: 'Seleccion multiple desde opciones', label: 'Selector multiple', properties: ['options'], visibility: { outputType: 'string[]', operators: ['includes', 'notIncludes', 'isEmpty', 'isNotEmpty'] } },
8885
+ { value: 'date', icon: 'ri-calendar-2-line', detail: 'Selector de fecha o mes', label: 'Fecha', properties: ['monthMode', 'monthDay', 'minDate', 'maxDate'], visibility: { outputType: 'string yyyy-MM-dd', operators: ['equals', 'notEquals', 'greaterThan', 'lessThan', 'isEmpty', 'isNotEmpty'] } },
8886
+ { value: 'time', icon: 'ri-timer-2-line', detail: 'Selector de hora', label: 'Hora', properties: ['timeInterval'], visibility: { outputType: 'string HH:mm', operators: ['equals', 'notEquals', 'greaterThan', 'lessThan', 'isEmpty', 'isNotEmpty'] } },
8887
+ { value: 'radio', icon: 'ri-radio-button-line', detail: 'Seleccion unica entre opciones', label: 'Radio', properties: ['options'], visibility: { outputType: 'string', operators: ['equals', 'notEquals', 'includes', 'notIncludes', 'isEmpty', 'isNotEmpty'] } },
8888
+ { value: 'checkbox', icon: 'ri-checkbox-line', detail: 'Campo booleano tipo check', label: 'Checkbox', properties: ['placeholder'], visibility: { outputType: 'boolean', operators: ['equals', 'notEquals', 'isEmpty', 'isNotEmpty'] } },
8889
+ { value: 'switch', icon: 'ri-toggle-line', detail: 'Campo booleano tipo switch', label: 'Switch', properties: ['placeholder'], visibility: { outputType: 'boolean', operators: ['equals', 'notEquals', 'isEmpty', 'isNotEmpty'] } },
8890
+ { value: 'pool', icon: 'ri-bubble-chart-line', detail: 'Seleccion con lista y detalle por item', label: 'Pool de opciones', properties: ['options', 'multyEnabled', 'poolEnabledListView', 'poolTitle'], visibility: { outputType: 'selectedDetailed[]', operators: ['isEmpty', 'isNotEmpty'] } },
8891
+ { value: 'file', icon: 'ri-attachment-line', detail: 'Carga de archivos', label: 'Archivo', properties: ['multyEnabled', 'fileTypes', 'iaValidation', 'iaValidationPrompt'], visibility: { outputType: 'UicFileInputValue[]', operators: ['isEmpty', 'isNotEmpty'] } },
8892
+ { value: 'searcher', icon: 'ri-seo-line', detail: 'Buscador con fuente de datos externa', label: 'Buscador', properties: ['placeholder', 'optionsSource.key', 'optionsSource.idField', 'optionsSource.textField', 'optionsSource.textTemplate', 'optionsSource.dependsOn', 'optionsSource.paramName'], visibility: { outputType: 'object', operators: ['isEmpty', 'isNotEmpty'] } },
8893
+ { value: 'slider', icon: 'ri-equalizer-2-line', detail: 'Control deslizante continuo', label: 'Deslizador', properties: ['min', 'max', 'sliderInterval', 'sliderMarks'], visibility: { outputType: 'number', operators: ['equals', 'notEquals', 'greaterThan', 'lessThan', 'isEmpty', 'isNotEmpty'] } },
8894
+ { value: 'repeater', icon: 'ri-stack-line', detail: 'Grupo de campos repetibles', label: 'Repetidor', properties: ['repeaterMinItems', 'repeaterMaxItems', 'repeaterAddLabel', 'repeaterRemoveLabel', 'repeaterItemTitle'], visibility: { outputType: 'object[]', operators: ['isEmpty', 'isNotEmpty'] } }
8491
8895
  ];
8492
8896
  const BASE_FORM_FIELDS = [
8493
8897
  {
@@ -8698,6 +9102,11 @@ const EXTRA_FORM_FIELDS = [
8698
9102
  fieldName: 'optionsSource.key',
8699
9103
  operator: 'notEquals',
8700
9104
  value: null
9105
+ },
9106
+ {
9107
+ fieldName: 'optionsSource.dependsOn',
9108
+ operator: 'notEquals',
9109
+ value: ''
8701
9110
  }
8702
9111
  ]
8703
9112
  },
@@ -8922,6 +9331,42 @@ const EXTRA_FORM_FIELDS = [
8922
9331
  internalIcon: 'ri-folder-add-line',
8923
9332
  tip: 'form_builder.extra.multyEnabled_tip',
8924
9333
  type: 'checkbox'
9334
+ },
9335
+ {
9336
+ name: 'repeaterMinItems',
9337
+ label: 'form_builder.extra.repeaterMinItems',
9338
+ internalIcon: 'ri-skip-down-line',
9339
+ tip: 'form_builder.extra.repeaterMinItems_tip',
9340
+ type: 'number',
9341
+ step: 1,
9342
+ min: 0
9343
+ },
9344
+ {
9345
+ name: 'repeaterMaxItems',
9346
+ label: 'form_builder.extra.repeaterMaxItems',
9347
+ internalIcon: 'ri-skip-up-line',
9348
+ tip: 'form_builder.extra.repeaterMaxItems_tip',
9349
+ type: 'number',
9350
+ step: 1,
9351
+ min: 1
9352
+ },
9353
+ {
9354
+ name: 'repeaterAddLabel',
9355
+ label: 'form_builder.extra.repeaterAddLabel',
9356
+ internalIcon: 'ri-add-circle-line',
9357
+ type: 'text'
9358
+ },
9359
+ {
9360
+ name: 'repeaterRemoveLabel',
9361
+ label: 'form_builder.extra.repeaterRemoveLabel',
9362
+ internalIcon: 'ri-delete-bin-line',
9363
+ type: 'text'
9364
+ },
9365
+ {
9366
+ name: 'repeaterItemTitle',
9367
+ label: 'form_builder.extra.repeaterItemTitle',
9368
+ internalIcon: 'ri-price-tag-2-line',
9369
+ type: 'text'
8925
9370
  }
8926
9371
  ];
8927
9372
  const BRANCH_FORM_FIELDS = [
@@ -8938,12 +9383,7 @@ const BRANCH_FORM_FIELDS = [
8938
9383
  label: 'form_builder.field_editor.operator',
8939
9384
  type: 'select',
8940
9385
  options: [
8941
- { id: 'equals', text: 'equals' },
8942
- { id: 'notEquals', text: 'notEquals' },
8943
- { id: 'greaterThan', text: 'greaterThan' },
8944
- { id: 'lessThan', text: 'lessThan' },
8945
- { id: 'includes', text: 'includes' },
8946
- { id: 'notIncludes', text: 'notIncludes' }
9386
+ ...VISIBILITY_OPERATOR_OPTIONS
8947
9387
  ],
8948
9388
  required: true
8949
9389
  },
@@ -9035,8 +9475,10 @@ class FieldEditorComponent {
9035
9475
  return !!fieldType && (fieldType.value === 'searcher' || fieldType.properties.includes('options'));
9036
9476
  });
9037
9477
  hasExternalOptionsSource = computed(() => !!this.localField().optionsSource?.key);
9478
+ isRepeater = computed(() => this.fieldType()?.value === 'repeater');
9038
9479
  config = input.required();
9039
9480
  focusRequiredField = input(false);
9481
+ isSubField = input(false);
9040
9482
  options = input({});
9041
9483
  hiddenSections = signal([]);
9042
9484
  fieldChange = output();
@@ -9046,20 +9488,14 @@ class FieldEditorComponent {
9046
9488
  optionsSourceFields = computed(() => this.buildOptionsSourceFields());
9047
9489
  advancedFields = computed(() => (this.buildAdvancedBlocks()));
9048
9490
  styleFields = computed(() => (this.buildDesignBlocks()));
9049
- branchFields = this.buildBranchFields();
9491
+ branchDraftValues = signal({});
9492
+ branchFields = computed(() => this.buildBranchFields(this.branchDraftValues()));
9050
9493
  initialValues = computed(() => {
9051
9494
  const fieldData = this.config().fieldData;
9052
9495
  return fieldData ?? {};
9053
9496
  });
9054
9497
  branchInitialValues = computed(() => {
9055
- const firstRule = this.localField().visibilityRules?.[0];
9056
- if (!firstRule)
9057
- return {};
9058
- return {
9059
- fieldName: firstRule.fieldName ?? null,
9060
- operator: firstRule.operator ?? null,
9061
- value: firstRule.value ?? null
9062
- };
9498
+ return this.branchDraftValues();
9063
9499
  });
9064
9500
  /*
9065
9501
  {
@@ -9077,6 +9513,7 @@ class FieldEditorComponent {
9077
9513
  this.localField.set(field);
9078
9514
  if (this.currentFieldCode !== config.code) {
9079
9515
  this.currentFieldCode = config.code;
9516
+ this.branchDraftValues.set(this.ruleToBranchValues(field.visibilityRules?.[0]));
9080
9517
  this.scrollPropertiesToTop();
9081
9518
  }
9082
9519
  });
@@ -9089,8 +9526,23 @@ class FieldEditorComponent {
9089
9526
  }
9090
9527
  updateRuleValue(fr) {
9091
9528
  const fieldName = fr['fieldName'];
9092
- const operator = fr['operator'];
9093
- const value = fr['value'];
9529
+ const previousDraft = this.branchDraftValues();
9530
+ const parentChanged = fieldName !== previousDraft['fieldName'];
9531
+ const selectedOperator = (parentChanged ? null : fr['operator']);
9532
+ const allowedOperators = this.getAllowedVisibilityOperators(this.getDependencyFieldType(fieldName));
9533
+ const operator = selectedOperator && allowedOperators.includes(selectedOperator)
9534
+ ? selectedOperator
9535
+ : null;
9536
+ const value = parentChanged || this.isValueLessVisibilityOperator(operator) ? null : fr['value'];
9537
+ const nextDraft = {
9538
+ fieldName: fieldName ?? null,
9539
+ operator,
9540
+ value
9541
+ };
9542
+ this.branchDraftValues.set(nextDraft);
9543
+ if (parentChanged) {
9544
+ this.clearCurrentVisibilityRule();
9545
+ }
9094
9546
  // Ignore partial emissions while the dependency form is still incomplete.
9095
9547
  if (!fieldName || !operator)
9096
9548
  return;
@@ -9119,6 +9571,7 @@ class FieldEditorComponent {
9119
9571
  ...this.localField(),
9120
9572
  visibilityRules: []
9121
9573
  };
9574
+ this.branchDraftValues.set({});
9122
9575
  this.localField.set(updatedField);
9123
9576
  this.fieldChange.emit(updatedField);
9124
9577
  }
@@ -9127,6 +9580,7 @@ class FieldEditorComponent {
9127
9580
  ...this.localField(),
9128
9581
  visibilityRules: null
9129
9582
  };
9583
+ this.branchDraftValues.set({});
9130
9584
  this.localField.set(updatedField);
9131
9585
  this.fieldChange.emit(updatedField);
9132
9586
  }
@@ -9211,15 +9665,99 @@ class FieldEditorComponent {
9211
9665
  }
9212
9666
  return field;
9213
9667
  }
9214
- buildBranchFields() {
9668
+ buildBranchFields(values) {
9669
+ const fieldName = values['fieldName'];
9670
+ const dependencyType = this.getDependencyFieldType(fieldName);
9671
+ const allowedOperators = this.getAllowedVisibilityOperators(dependencyType);
9215
9672
  return BRANCH_FORM_FIELDS.map(field => ({
9216
- ...field,
9673
+ ...this.prepareBranchField(field, fieldName, allowedOperators),
9217
9674
  label: this.translateService.translate(field.label ?? ''),
9218
9675
  placeholder: field.placeholder ? this.translateService.translate(field.placeholder) : this.translateService.translate(field.label ?? '')
9219
9676
  }));
9220
9677
  }
9678
+ prepareBranchField(field, fieldName, allowedOperators) {
9679
+ if (field.name === 'operator') {
9680
+ return {
9681
+ ...field,
9682
+ options: VISIBILITY_OPERATOR_OPTIONS
9683
+ .filter(option => allowedOperators.includes(option.id))
9684
+ .map(option => ({
9685
+ ...option,
9686
+ text: this.translateService.translate(`form_builder.visibility_operators.${option.id}`)
9687
+ })),
9688
+ visibilityRules: [
9689
+ { fieldName: 'fieldName', operator: 'notEquals', value: null },
9690
+ { fieldName: 'fieldName', operator: 'notEquals', value: '' }
9691
+ ]
9692
+ };
9693
+ }
9694
+ if (field.name === 'value') {
9695
+ const valueField = {
9696
+ ...field,
9697
+ visibilityRules: [
9698
+ { fieldName: 'fieldName', operator: 'notEquals', value: null },
9699
+ { fieldName: 'fieldName', operator: 'notEquals', value: '' },
9700
+ { fieldName: 'operator', operator: 'notEquals', value: null },
9701
+ { fieldName: 'operator', operator: 'notEquals', value: '' },
9702
+ { fieldName: 'operator', operator: 'notEquals', value: 'isEmpty' },
9703
+ { fieldName: 'operator', operator: 'notEquals', value: 'isNotEmpty' }
9704
+ ],
9705
+ type: this.getBranchValueFieldType(fieldName)
9706
+ };
9707
+ if (valueField.type === 'select') {
9708
+ valueField.options = [
9709
+ { id: 'true', text: 'true' },
9710
+ { id: 'false', text: 'false' }
9711
+ ];
9712
+ }
9713
+ return valueField;
9714
+ }
9715
+ return field;
9716
+ }
9717
+ getAllowedVisibilityOperators(fieldType) {
9718
+ return FIELDS_CONFIG.find(field => field.value === fieldType)?.visibility.operators ?? [];
9719
+ }
9720
+ getDependencyFieldType(fieldName) {
9721
+ const option = this.options()['fieldName']?.find(item => item.id === fieldName);
9722
+ const type = option?.fieldType;
9723
+ return FIELDS_CONFIG.some(field => field.value === type) ? type : null;
9724
+ }
9725
+ getBranchValueFieldType(fieldName) {
9726
+ const dependencyType = this.getDependencyFieldType(fieldName);
9727
+ if (dependencyType === 'number' || dependencyType === 'slider')
9728
+ return 'number';
9729
+ if (dependencyType === 'date')
9730
+ return 'date';
9731
+ if (dependencyType === 'time')
9732
+ return 'time';
9733
+ if (dependencyType === 'checkbox' || dependencyType === 'switch')
9734
+ return 'select';
9735
+ return 'text';
9736
+ }
9737
+ ruleToBranchValues(rule) {
9738
+ if (!rule)
9739
+ return {};
9740
+ return {
9741
+ fieldName: rule.fieldName ?? null,
9742
+ operator: rule.operator ?? null,
9743
+ value: rule.value ?? null
9744
+ };
9745
+ }
9746
+ clearCurrentVisibilityRule() {
9747
+ if (!this.localField().visibilityRules?.length)
9748
+ return;
9749
+ const updatedField = {
9750
+ ...this.localField(),
9751
+ visibilityRules: []
9752
+ };
9753
+ this.localField.set(updatedField);
9754
+ this.fieldChange.emit(updatedField);
9755
+ }
9756
+ isValueLessVisibilityOperator(operator) {
9757
+ return operator === 'isEmpty' || operator === 'isNotEmpty';
9758
+ }
9221
9759
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FieldEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9222
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FieldEditorComponent, isStandalone: true, selector: "lib-field-editor", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, focusRequiredField: { classPropertyName: "focusRequiredField", publicName: "focusRequiredField", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fieldChange: "fieldChange" }, ngImport: i0, template: "<div class=\"props-title\">{{'form_builder.field_editor.basic' | uicTranslate}} <i (click)=\"toggleSection('basic')\" class=\"{{hiddenSections().includes('basic') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('basic')\">\n <ui-form-wrapper\n [fields]=\"requiredFields\" \n [initialValues]=\"initialValues()\"\n [focusFieldName]=\"focusRequiredField() ? 'label' : null\"\n [focusFieldTrigger]=\"focusRequiredField() ? config().code : null\"\n [cols]=\"1\" \n (formChange)=\"updateFieldValues($event)\">\n </ui-form-wrapper>\n @if (hasOptionsSourceConfig()) {\n <ui-form-wrapper\n [fields]=\"optionsSourceFields()\"\n [initialValues]=\"initialValues()\"\n [externalData]=\"options()\"\n [cols]=\"1\"\n (formChange)=\"updateFieldValues($event)\">\n </ui-form-wrapper>\n @if (hasOptions() && !hasExternalOptionsSource()) {\n <ui-field-options-editor [options]=\"localField().options ?? []\" (optionsChange)=\"updateOptions($event)\"></ui-field-options-editor>\n }\n }\n</div>\n\n<div class=\"props-title\">{{'form_builder.field_editor.advanced' | uicTranslate}} <i (click)=\"toggleSection('advanced')\" class=\"{{hiddenSections().includes('advanced') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('advanced')\">\n <ui-form-wrapper\n [fields]=\"advancedFields()\" \n [initialValues]=\"initialValues()\"\n [cols]=\"1\" \n (formChange)=\"updateFieldValues($event)\">\n </ui-form-wrapper>\n</div>\n\n<div class=\"props-title\">{{'form_builder.field_editor.style' | uicTranslate}} <i (click)=\"toggleSection('style')\" class=\"{{hiddenSections().includes('style') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('style')\">\n <ui-form-wrapper\n [fields]=\"styleFields()\" \n [initialValues]=\"initialValues()\"\n [cols]=\"1\" \n (formChange)=\"updateFieldValues($event)\"> \n </ui-form-wrapper>\n</div>\n\n<div class=\"props-title\">{{'form_builder.field_editor.dependency_title' | uicTranslate}} <i (click)=\"toggleSection('dependency')\" class=\"{{hiddenSections().includes('dependency') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('dependency')\">\n @if (localField().visibilityRules ) {\n <p>{{'form_builder.field_editor.dependency_description' | uicTranslate}}</p>\n <ui-form-wrapper\n [fields]=\"branchFields\" \n [initialValues]=\"branchInitialValues()\"\n [externalData]=\"options()\"\n [cols]=\"1\" \n (formChange)=\"updateRuleValue($event)\">\n </ui-form-wrapper>\n <ui-button color=\"black\" size=\"s\" (click)=\"removeRule()\">{{'form_builder.field_editor.remove_relation' | uicTranslate}}</ui-button>\n }@else {\n <ui-button color=\"black\" size=\"s\" (click)=\"addRule()\">{{'form_builder.field_editor.add_relation' | uicTranslate}}</ui-button>\n }\n</div>\n", styles: [".hidden{display:none}.props-title{font-size:14px;font-weight:600;margin-bottom:8px;background:var(--grey-200);padding:5px;border-radius:5px;display:flex;justify-content:space-between}.props-title i{cursor:pointer}.props-inputs{padding-left:20px;margin-bottom:10px}p{font-size:14px;margin:10px 0;color:var(--yellow-800)}\n"], dependencies: [{ kind: "component", type: UicFormWrapperComponent, selector: "ui-form-wrapper", inputs: ["schema", "fields", "cols", "externalData", "selectOptionsResolver", "loading", "disabled", "showButtons", "fillSelects", "initialValues", "focusFieldName", "focusFieldTrigger", "fileUidResolverFn"], outputs: ["formSubmit", "formChange", "optionsSourceError"] }, { kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }, { kind: "component", type: UicFieldOptionsEditorComponent, selector: "ui-field-options-editor", inputs: ["options"], outputs: ["optionsChange"] }] });
9760
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FieldEditorComponent, isStandalone: true, selector: "lib-field-editor", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, focusRequiredField: { classPropertyName: "focusRequiredField", publicName: "focusRequiredField", isSignal: true, isRequired: false, transformFunction: null }, isSubField: { classPropertyName: "isSubField", publicName: "isSubField", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fieldChange: "fieldChange" }, ngImport: i0, template: "<div class=\"props-title\">{{'form_builder.field_editor.basic' | uicTranslate}} <i (click)=\"toggleSection('basic')\" class=\"{{hiddenSections().includes('basic') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\r\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('basic')\">\r\n <ui-form-wrapper\r\n [fields]=\"requiredFields\" \r\n [initialValues]=\"initialValues()\"\r\n [focusFieldName]=\"focusRequiredField() ? 'label' : null\"\r\n [focusFieldTrigger]=\"focusRequiredField() ? config().code : null\"\r\n [cols]=\"1\" \r\n (formChange)=\"updateFieldValues($event)\">\r\n </ui-form-wrapper>\r\n @if (hasOptionsSourceConfig()) {\r\n <ui-form-wrapper\r\n [fields]=\"optionsSourceFields()\"\r\n [initialValues]=\"initialValues()\"\r\n [externalData]=\"options()\"\r\n [cols]=\"1\"\r\n (formChange)=\"updateFieldValues($event)\">\r\n </ui-form-wrapper>\r\n @if (hasOptions() && !hasExternalOptionsSource()) {\r\n <ui-field-options-editor [options]=\"localField().options ?? []\" (optionsChange)=\"updateOptions($event)\"></ui-field-options-editor>\r\n }\r\n }\r\n</div>\r\n\r\n<div class=\"props-title\">{{'form_builder.field_editor.advanced' | uicTranslate}} <i (click)=\"toggleSection('advanced')\" class=\"{{hiddenSections().includes('advanced') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\r\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('advanced')\">\r\n <ui-form-wrapper\r\n [fields]=\"advancedFields()\" \r\n [initialValues]=\"initialValues()\"\r\n [cols]=\"1\" \r\n (formChange)=\"updateFieldValues($event)\">\r\n </ui-form-wrapper>\r\n</div>\r\n\r\n<div class=\"props-title\">{{'form_builder.field_editor.style' | uicTranslate}} <i (click)=\"toggleSection('style')\" class=\"{{hiddenSections().includes('style') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\r\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('style')\">\r\n <ui-form-wrapper\r\n [fields]=\"styleFields()\" \r\n [initialValues]=\"initialValues()\"\r\n [cols]=\"1\" \r\n (formChange)=\"updateFieldValues($event)\"> \r\n </ui-form-wrapper>\r\n</div>\r\n\r\n@if (!isSubField()) {\r\n <div class=\"props-title\">{{'form_builder.field_editor.dependency_title' | uicTranslate}} <i (click)=\"toggleSection('dependency')\" class=\"{{hiddenSections().includes('dependency') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\r\n <div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('dependency')\">\r\n @if (localField().visibilityRules ) {\r\n <p>{{'form_builder.field_editor.dependency_description' | uicTranslate}}</p>\r\n <ui-form-wrapper\r\n [fields]=\"branchFields()\" \r\n [initialValues]=\"branchInitialValues()\"\r\n [externalData]=\"options()\"\r\n [cols]=\"1\" \r\n (formChange)=\"updateRuleValue($event)\">\r\n </ui-form-wrapper>\r\n <ui-button color=\"black\" size=\"s\" (click)=\"removeRule()\">{{'form_builder.field_editor.remove_relation' | uicTranslate}}</ui-button>\r\n }@else {\r\n <ui-button color=\"black\" size=\"s\" (click)=\"addRule()\">{{'form_builder.field_editor.add_relation' | uicTranslate}}</ui-button>\r\n }\r\n </div>\r\n}\r\n", styles: [".hidden{display:none}.props-title{font-size:14px;font-weight:600;margin-bottom:8px;background:var(--grey-200);padding:5px;border-radius:5px;display:flex;justify-content:space-between}.props-title i{cursor:pointer}.props-inputs{padding-left:20px;margin-bottom:10px}p{font-size:14px;margin:10px 0;color:var(--yellow-800)}\n"], dependencies: [{ kind: "component", type: UicFormWrapperComponent, selector: "ui-form-wrapper", inputs: ["schema", "fields", "cols", "externalData", "selectOptionsResolver", "loading", "disabled", "showButtons", "fillSelects", "initialValues", "focusFieldName", "focusFieldTrigger", "fileUidResolverFn"], outputs: ["formSubmit", "formChange", "optionsSourceError"] }, { kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }, { kind: "component", type: UicFieldOptionsEditorComponent, selector: "ui-field-options-editor", inputs: ["options"], outputs: ["optionsChange"] }] });
9223
9761
  }
9224
9762
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FieldEditorComponent, decorators: [{
9225
9763
  type: Component,
@@ -9228,21 +9766,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
9228
9766
  UicButtonComponent,
9229
9767
  UicTranslatePipe,
9230
9768
  UicFieldOptionsEditorComponent
9231
- ], template: "<div class=\"props-title\">{{'form_builder.field_editor.basic' | uicTranslate}} <i (click)=\"toggleSection('basic')\" class=\"{{hiddenSections().includes('basic') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('basic')\">\n <ui-form-wrapper\n [fields]=\"requiredFields\" \n [initialValues]=\"initialValues()\"\n [focusFieldName]=\"focusRequiredField() ? 'label' : null\"\n [focusFieldTrigger]=\"focusRequiredField() ? config().code : null\"\n [cols]=\"1\" \n (formChange)=\"updateFieldValues($event)\">\n </ui-form-wrapper>\n @if (hasOptionsSourceConfig()) {\n <ui-form-wrapper\n [fields]=\"optionsSourceFields()\"\n [initialValues]=\"initialValues()\"\n [externalData]=\"options()\"\n [cols]=\"1\"\n (formChange)=\"updateFieldValues($event)\">\n </ui-form-wrapper>\n @if (hasOptions() && !hasExternalOptionsSource()) {\n <ui-field-options-editor [options]=\"localField().options ?? []\" (optionsChange)=\"updateOptions($event)\"></ui-field-options-editor>\n }\n }\n</div>\n\n<div class=\"props-title\">{{'form_builder.field_editor.advanced' | uicTranslate}} <i (click)=\"toggleSection('advanced')\" class=\"{{hiddenSections().includes('advanced') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('advanced')\">\n <ui-form-wrapper\n [fields]=\"advancedFields()\" \n [initialValues]=\"initialValues()\"\n [cols]=\"1\" \n (formChange)=\"updateFieldValues($event)\">\n </ui-form-wrapper>\n</div>\n\n<div class=\"props-title\">{{'form_builder.field_editor.style' | uicTranslate}} <i (click)=\"toggleSection('style')\" class=\"{{hiddenSections().includes('style') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('style')\">\n <ui-form-wrapper\n [fields]=\"styleFields()\" \n [initialValues]=\"initialValues()\"\n [cols]=\"1\" \n (formChange)=\"updateFieldValues($event)\"> \n </ui-form-wrapper>\n</div>\n\n<div class=\"props-title\">{{'form_builder.field_editor.dependency_title' | uicTranslate}} <i (click)=\"toggleSection('dependency')\" class=\"{{hiddenSections().includes('dependency') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('dependency')\">\n @if (localField().visibilityRules ) {\n <p>{{'form_builder.field_editor.dependency_description' | uicTranslate}}</p>\n <ui-form-wrapper\n [fields]=\"branchFields\" \n [initialValues]=\"branchInitialValues()\"\n [externalData]=\"options()\"\n [cols]=\"1\" \n (formChange)=\"updateRuleValue($event)\">\n </ui-form-wrapper>\n <ui-button color=\"black\" size=\"s\" (click)=\"removeRule()\">{{'form_builder.field_editor.remove_relation' | uicTranslate}}</ui-button>\n }@else {\n <ui-button color=\"black\" size=\"s\" (click)=\"addRule()\">{{'form_builder.field_editor.add_relation' | uicTranslate}}</ui-button>\n }\n</div>\n", styles: [".hidden{display:none}.props-title{font-size:14px;font-weight:600;margin-bottom:8px;background:var(--grey-200);padding:5px;border-radius:5px;display:flex;justify-content:space-between}.props-title i{cursor:pointer}.props-inputs{padding-left:20px;margin-bottom:10px}p{font-size:14px;margin:10px 0;color:var(--yellow-800)}\n"] }]
9769
+ ], template: "<div class=\"props-title\">{{'form_builder.field_editor.basic' | uicTranslate}} <i (click)=\"toggleSection('basic')\" class=\"{{hiddenSections().includes('basic') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\r\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('basic')\">\r\n <ui-form-wrapper\r\n [fields]=\"requiredFields\" \r\n [initialValues]=\"initialValues()\"\r\n [focusFieldName]=\"focusRequiredField() ? 'label' : null\"\r\n [focusFieldTrigger]=\"focusRequiredField() ? config().code : null\"\r\n [cols]=\"1\" \r\n (formChange)=\"updateFieldValues($event)\">\r\n </ui-form-wrapper>\r\n @if (hasOptionsSourceConfig()) {\r\n <ui-form-wrapper\r\n [fields]=\"optionsSourceFields()\"\r\n [initialValues]=\"initialValues()\"\r\n [externalData]=\"options()\"\r\n [cols]=\"1\"\r\n (formChange)=\"updateFieldValues($event)\">\r\n </ui-form-wrapper>\r\n @if (hasOptions() && !hasExternalOptionsSource()) {\r\n <ui-field-options-editor [options]=\"localField().options ?? []\" (optionsChange)=\"updateOptions($event)\"></ui-field-options-editor>\r\n }\r\n }\r\n</div>\r\n\r\n<div class=\"props-title\">{{'form_builder.field_editor.advanced' | uicTranslate}} <i (click)=\"toggleSection('advanced')\" class=\"{{hiddenSections().includes('advanced') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\r\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('advanced')\">\r\n <ui-form-wrapper\r\n [fields]=\"advancedFields()\" \r\n [initialValues]=\"initialValues()\"\r\n [cols]=\"1\" \r\n (formChange)=\"updateFieldValues($event)\">\r\n </ui-form-wrapper>\r\n</div>\r\n\r\n<div class=\"props-title\">{{'form_builder.field_editor.style' | uicTranslate}} <i (click)=\"toggleSection('style')\" class=\"{{hiddenSections().includes('style') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\r\n<div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('style')\">\r\n <ui-form-wrapper\r\n [fields]=\"styleFields()\" \r\n [initialValues]=\"initialValues()\"\r\n [cols]=\"1\" \r\n (formChange)=\"updateFieldValues($event)\"> \r\n </ui-form-wrapper>\r\n</div>\r\n\r\n@if (!isSubField()) {\r\n <div class=\"props-title\">{{'form_builder.field_editor.dependency_title' | uicTranslate}} <i (click)=\"toggleSection('dependency')\" class=\"{{hiddenSections().includes('dependency') ? 'ri-arrow-down-s-line' : 'ri-arrow-up-s-line'}}\"></i> </div>\r\n <div class=\"props-inputs\" [class.hidden]=\"hiddenSections().includes('dependency')\">\r\n @if (localField().visibilityRules ) {\r\n <p>{{'form_builder.field_editor.dependency_description' | uicTranslate}}</p>\r\n <ui-form-wrapper\r\n [fields]=\"branchFields()\" \r\n [initialValues]=\"branchInitialValues()\"\r\n [externalData]=\"options()\"\r\n [cols]=\"1\" \r\n (formChange)=\"updateRuleValue($event)\">\r\n </ui-form-wrapper>\r\n <ui-button color=\"black\" size=\"s\" (click)=\"removeRule()\">{{'form_builder.field_editor.remove_relation' | uicTranslate}}</ui-button>\r\n }@else {\r\n <ui-button color=\"black\" size=\"s\" (click)=\"addRule()\">{{'form_builder.field_editor.add_relation' | uicTranslate}}</ui-button>\r\n }\r\n </div>\r\n}\r\n", styles: [".hidden{display:none}.props-title{font-size:14px;font-weight:600;margin-bottom:8px;background:var(--grey-200);padding:5px;border-radius:5px;display:flex;justify-content:space-between}.props-title i{cursor:pointer}.props-inputs{padding-left:20px;margin-bottom:10px}p{font-size:14px;margin:10px 0;color:var(--yellow-800)}\n"] }]
9232
9770
  }], ctorParameters: () => [] });
9233
9771
 
9234
9772
  class FieldTypeSelectorComponent {
9235
- types = [...FIELDS_CONFIG];
9773
+ excludeTypes = input([]);
9774
+ types = computed(() => FIELDS_CONFIG.filter(f => !this.excludeTypes().includes(f.value)));
9236
9775
  selectType = output();
9237
9776
  addField(type) {
9238
9777
  this.selectType.emit(type);
9239
9778
  }
9240
9779
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FieldTypeSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9241
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FieldTypeSelectorComponent, isStandalone: true, selector: "lib-field-type-selector", outputs: { selectType: "selectType" }, ngImport: i0, template: "@for (type of types; track $index) {\r\n <div [uiTooltip]=\"type.label | uicTranslate\" (click)=\"addField(type)\" class=\"field-type\">\n <i [class]=\"type.icon\"></i>\r\n <!-- <div>\r\n <b>{{type.label}}</b>\r\n <p>{{type.detail}}</p>\r\n </div> -->\r\n </div>\r\n}\n", styles: [":host{display:flex;overflow:auto;gap:5px}.field-type{border:solid 1px var(--grey-300);border-radius:5px;padding:5px;display:flex;align-items:center;background-color:#fff;cursor:pointer;gap:5px}.field-type b{font-weight:400}.field-type p{font-size:12px;color:var(--grey-400)}\n"], dependencies: [{ kind: "directive", type: UicToolTipDirective, selector: "[uiTooltip]", inputs: ["uiTooltip"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }] });
9780
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FieldTypeSelectorComponent, isStandalone: true, selector: "lib-field-type-selector", inputs: { excludeTypes: { classPropertyName: "excludeTypes", publicName: "excludeTypes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectType: "selectType" }, ngImport: i0, template: "@for (type of types(); track $index) {\r\n <div [uiTooltip]=\"type.label | uicTranslate\" (click)=\"addField(type)\" class=\"field-type\">\r\n <i [class]=\"type.icon\"></i>\r\n </div>\r\n}\r\n", styles: [":host{display:flex;overflow:auto;gap:5px}.field-type{border:solid 1px var(--grey-300);border-radius:5px;padding:5px;display:flex;align-items:center;background-color:#fff;cursor:pointer;gap:5px}.field-type b{font-weight:400}.field-type p{font-size:12px;color:var(--grey-400)}\n"], dependencies: [{ kind: "directive", type: UicToolTipDirective, selector: "[uiTooltip]", inputs: ["uiTooltip"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }] });
9242
9781
  }
9243
9782
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FieldTypeSelectorComponent, decorators: [{
9244
9783
  type: Component,
9245
- args: [{ selector: 'lib-field-type-selector', imports: [UicToolTipDirective, UicTranslatePipe], template: "@for (type of types; track $index) {\r\n <div [uiTooltip]=\"type.label | uicTranslate\" (click)=\"addField(type)\" class=\"field-type\">\n <i [class]=\"type.icon\"></i>\r\n <!-- <div>\r\n <b>{{type.label}}</b>\r\n <p>{{type.detail}}</p>\r\n </div> -->\r\n </div>\r\n}\n", styles: [":host{display:flex;overflow:auto;gap:5px}.field-type{border:solid 1px var(--grey-300);border-radius:5px;padding:5px;display:flex;align-items:center;background-color:#fff;cursor:pointer;gap:5px}.field-type b{font-weight:400}.field-type p{font-size:12px;color:var(--grey-400)}\n"] }]
9784
+ args: [{ selector: 'lib-field-type-selector', imports: [UicToolTipDirective, UicTranslatePipe], template: "@for (type of types(); track $index) {\r\n <div [uiTooltip]=\"type.label | uicTranslate\" (click)=\"addField(type)\" class=\"field-type\">\r\n <i [class]=\"type.icon\"></i>\r\n </div>\r\n}\r\n", styles: [":host{display:flex;overflow:auto;gap:5px}.field-type{border:solid 1px var(--grey-300);border-radius:5px;padding:5px;display:flex;align-items:center;background-color:#fff;cursor:pointer;gap:5px}.field-type b{font-weight:400}.field-type p{font-size:12px;color:var(--grey-400)}\n"] }]
9246
9785
  }] });
9247
9786
 
9248
9787
  class BlockEditorComponent {
@@ -9257,9 +9796,81 @@ class BlockEditorComponent {
9257
9796
  addFieldRequest = output();
9258
9797
  deleteBlock = output();
9259
9798
  notifySelectedField = output();
9799
+ notifySelectedSubField = output();
9260
9800
  selectedFieldId = null;
9801
+ selectedSubFieldCode = null;
9802
+ visibilityParentFieldCodes = new Set();
9803
+ excludeSubFieldTypes = ['repeater'];
9804
+ expandedRepeaters = signal(new Set());
9805
+ isRepeaterExpanded(code) {
9806
+ return this.expandedRepeaters().has(code);
9807
+ }
9808
+ toggleRepeater(code, event) {
9809
+ event.stopPropagation();
9810
+ const next = new Set(this.expandedRepeaters());
9811
+ next.has(code) ? next.delete(code) : next.add(code);
9812
+ this.expandedRepeaters.set(next);
9813
+ }
9814
+ isRepeaterParentSelected(field) {
9815
+ if (this.selectedFieldId === field.code)
9816
+ return true;
9817
+ if (field.fieldData.type !== 'repeater')
9818
+ return false;
9819
+ return !!(this.selectedSubFieldCode && field.repeaterSubFields?.some(sf => sf.code === this.selectedSubFieldCode));
9820
+ }
9261
9821
  selectField(field) {
9262
9822
  this.notifySelectedField.emit(field);
9823
+ this.notifySelectedSubField.emit(null);
9824
+ }
9825
+ onSelectSubField(parentCode, subField, event) {
9826
+ event.stopPropagation();
9827
+ this.notifySelectedSubField.emit({ parentCode, subField });
9828
+ }
9829
+ addSubField(parentCode, type) {
9830
+ const parentField = this.block().fields.find(f => f.code === parentCode);
9831
+ const existing = parentField?.repeaterSubFields ?? [];
9832
+ const sfCode = this.generateSubFieldCode(existing);
9833
+ const newSf = {
9834
+ code: sfCode,
9835
+ order: existing.length + 1,
9836
+ type: type.value,
9837
+ field: type,
9838
+ fieldData: { name: sfCode, label: 'Nuevo campo', type: type.value }
9839
+ };
9840
+ this.updateSubFields(parentCode, [...existing, newSf]);
9841
+ this.notifySelectedSubField.emit({ parentCode, subField: newSf });
9842
+ }
9843
+ deleteSubField(parentCode, sfCode, event) {
9844
+ event.stopPropagation();
9845
+ const parentField = this.block().fields.find(f => f.code === parentCode);
9846
+ const updated = (parentField?.repeaterSubFields ?? []).filter(sf => sf.code !== sfCode);
9847
+ this.updateSubFields(parentCode, updated);
9848
+ if (this.selectedSubFieldCode === sfCode) {
9849
+ this.notifySelectedSubField.emit(null);
9850
+ }
9851
+ }
9852
+ reorderSubFields(parentCode, event) {
9853
+ if (event.previousIndex === event.currentIndex)
9854
+ return;
9855
+ const parentField = this.block().fields.find(f => f.code === parentCode);
9856
+ const next = [...(parentField?.repeaterSubFields ?? [])];
9857
+ moveItemInArray(next, event.previousIndex, event.currentIndex);
9858
+ this.updateSubFields(parentCode, next);
9859
+ }
9860
+ updateSubFields(parentCode, subFields) {
9861
+ this.blockChange.emit({
9862
+ ...this.block(),
9863
+ fields: this.block().fields.map(f => f.code !== parentCode ? f : { ...f, repeaterSubFields: subFields })
9864
+ });
9865
+ }
9866
+ generateSubFieldCode(existing) {
9867
+ const max = existing.reduce((m, sf) => {
9868
+ if (!sf.code.startsWith('sf_'))
9869
+ return m;
9870
+ const n = parseInt(sf.code.slice(3), 10);
9871
+ return isNaN(n) ? m : Math.max(m, n);
9872
+ }, 0);
9873
+ return `sf_${max + 1}`;
9263
9874
  }
9264
9875
  changeTitle(newTitle) {
9265
9876
  this.blockChange.emit({ ...this.block(), title: newTitle });
@@ -9270,13 +9881,27 @@ class BlockEditorComponent {
9270
9881
  addField(newFieldType) {
9271
9882
  this.addFieldRequest.emit(newFieldType);
9272
9883
  }
9884
+ requestDeleteBlock(e) {
9885
+ e.stopPropagation();
9886
+ if (this.isBlockVisibilityParent())
9887
+ return;
9888
+ this.deleteBlock.emit(this.block().code);
9889
+ }
9273
9890
  deleteField(fieldCode, e) {
9274
9891
  e.stopPropagation();
9892
+ if (this.isVisibilityParent(fieldCode))
9893
+ return;
9275
9894
  this.blockChange.emit({ ...this.block(), fields: this.block().fields.filter(f => f.code !== fieldCode) });
9276
9895
  if (this.selectedFieldId === fieldCode) {
9277
9896
  this.notifySelectedField.emit(null);
9278
9897
  }
9279
9898
  }
9899
+ isVisibilityParent(fieldCode) {
9900
+ return this.visibilityParentFieldCodes.has(fieldCode);
9901
+ }
9902
+ isBlockVisibilityParent() {
9903
+ return this.block().fields.some(field => this.isVisibilityParent(field.code));
9904
+ }
9280
9905
  reorderFields(event) {
9281
9906
  if (event.previousIndex === event.currentIndex)
9282
9907
  return;
@@ -9285,7 +9910,7 @@ class BlockEditorComponent {
9285
9910
  this.blockChange.emit({ ...this.block(), fields: nextFields });
9286
9911
  }
9287
9912
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BlockEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9288
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: BlockEditorComponent, isStandalone: true, selector: "lib-block-editor", inputs: { block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, selectedFieldId: { classPropertyName: "selectedFieldId", publicName: "selectedFieldId", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { blockChange: "blockChange", addFieldRequest: "addFieldRequest", deleteBlock: "deleteBlock", notifySelectedField: "notifySelectedField" }, ngImport: i0, template: "<div class=\"form-block-card\">\n <div class=\"form-block-card-header\">\n <div>\n <input [placeholder]=\"'form_builder.block_title' | uicTranslate\" [ngModel]=\"block().title\" (ngModelChange)=\"changeTitle($event)\">\n <input [placeholder]=\"'form_builder.block_subtitle' | uicTranslate\" [ngModel]=\"block().subtitle\" (ngModelChange)=\"changeSubtitle($event)\">\n </div>\n <ui-button [uiTooltip]=\"'form_builder.delete_block' | uicTranslate\" (click)=\"deleteBlock.emit(block().code)\" size=\"s\" icon=\"ri-delete-bin-line\" color=\"red\" type=\"ghost\" [iconOnly]=\"true\"></ui-button>\n </div>\n <div\n class=\"form-block-card-body\"\n cdkDropList\n [cdkDropListData]=\"block().fields\"\n (cdkDropListDropped)=\"reorderFields($event)\">\n \n <!-- FIELDS -->\n @for(edtField of block().fields; track edtField.code; let i = $index) {\n <div\n class=\"xfield-card\"\n cdkDrag\n [class.selected-field]=\"selectedFieldId === edtField.code\"\n (click)=\"selectField(edtField)\">\n <i class=\"ri-draggable field-drag-handle\" cdkDragHandle></i>\n\n <div class=\"xfield-card-icon\">\n <i [class]=\"edtField.field?.icon ?? ''\"></i> \n </div>\n <div class=\"xfield-card-body\">\n <b> {{edtField.fieldData.label}} \n @if(edtField.fieldData.visibilityRules) {\n <i class=\"branched-field ri-git-merge-line\"></i> \n <span class=\"vs-rule\"> {{edtField.fieldData.visibilityRules[0]?.fieldLabel ?? edtField.fieldData.visibilityRules[0]?.fieldName ?? '-'}} </span>\n }\n </b>\n <p> {{edtField.field?.label ?? edtField.type ?? edtField.fieldData.type}} </p>\n </div>\n <div class=\"xfield-card-actions\">\n <ui-button (click)=\"deleteField(edtField.code, $event)\" size=\"s\" icon=\"ri-delete-bin-line\" color=\"red\" type=\"ghost\" [iconOnly]=\"true\"></ui-button>\n </div>\n </div>\n\n }\n\n </div>\n <div class=\"new-field\">\n {{'form_builder.add_new_field' | uicTranslate}}\n <lib-field-type-selector (selectType)=\"addField($event)\"></lib-field-type-selector>\n </div>\n</div>\n", styles: [".form-block-card{border:solid 1px var(--blue-500);border-radius:5px;overflow:hidden}.form-block-card-header{display:flex;gap:5px;align-items:center;background-color:#fff;padding:10px;border-bottom:solid 1px var(--grey-300)}.form-block-card-header>div{flex:1 1;gap:4px;display:flex;flex-direction:column}.form-block-card-header>div input{border:solid 1px var(--grey-100);padding:3px 6px;border-radius:5px}.form-block-card-header>div input:focus{border:solid 1px var(--primary-500);outline:none}.form-block-card-body{padding:15px;display:flex;flex-direction:column;background-color:var(--grey-50);gap:10px}.xfield-card{background-color:#fff;border-radius:5px;padding:10px;gap:10px;display:flex;align-items:center;border:solid 2px var(--grey-200);cursor:pointer;transition:border-color .3s ease}.xfield-card:hover{border-color:var(--grey-300)}.xfield-card-icon{display:flex;justify-content:center;align-items:center;border-radius:5px;width:30px;height:30px;font-size:20px;background-color:var(--grey-300);color:var(--grey-600)}.xfield-card-body{flex:1 1}.xfield-card-body b{display:flex;align-items:center;gap:5px}.xfield-card-body p{color:var(--grey-400);font-size:12px}.field-drag-handle{cursor:grab;color:var(--grey-400);transition:color .2s ease}.field-drag-handle:active{cursor:grabbing}.xfield-card:hover .field-drag-handle{color:var(--grey-600)}.cdk-drag-preview{box-sizing:border-box;border-radius:5px;box-shadow:0 6px 12px #0000002e}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.form-block-card-body.cdk-drop-list-dragging .xfield-card:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.branched-field{color:var(--yellow-600)}.selected-field,.selected-field:hover{border-color:var(--primary-500)}.new-field{border-top:solid 1px var(--grey-300);padding:10px;background-color:#fff;display:flex;flex-direction:column;gap:5px;font-size:14px}.vs-rule{font-size:12px;color:var(--grey-600)}\n"], dependencies: [{ kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "directive", type: UicToolTipDirective, selector: "[uiTooltip]", inputs: ["uiTooltip"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1$3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i1$3.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FieldTypeSelectorComponent, selector: "lib-field-type-selector", outputs: ["selectType"] }] });
9913
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: BlockEditorComponent, isStandalone: true, selector: "lib-block-editor", inputs: { block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, selectedFieldId: { classPropertyName: "selectedFieldId", publicName: "selectedFieldId", isSignal: false, isRequired: false, transformFunction: null }, selectedSubFieldCode: { classPropertyName: "selectedSubFieldCode", publicName: "selectedSubFieldCode", isSignal: false, isRequired: false, transformFunction: null }, visibilityParentFieldCodes: { classPropertyName: "visibilityParentFieldCodes", publicName: "visibilityParentFieldCodes", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { blockChange: "blockChange", addFieldRequest: "addFieldRequest", deleteBlock: "deleteBlock", notifySelectedField: "notifySelectedField", notifySelectedSubField: "notifySelectedSubField" }, ngImport: i0, template: "<div class=\"form-block-card\">\r\n <div class=\"form-block-card-header\">\r\n <div>\r\n <input [placeholder]=\"'form_builder.block_title' | uicTranslate\" [ngModel]=\"block().title\" (ngModelChange)=\"changeTitle($event)\">\r\n <input [placeholder]=\"'form_builder.block_subtitle' | uicTranslate\" [ngModel]=\"block().subtitle\" (ngModelChange)=\"changeSubtitle($event)\">\r\n </div>\r\n <ui-button\r\n [uiTooltip]=\"(isBlockVisibilityParent() ? 'form_builder.delete_block_dependency_blocked' : 'form_builder.delete_block') | uicTranslate\"\r\n [disabled]=\"isBlockVisibilityParent()\"\r\n (click)=\"requestDeleteBlock($event)\"\r\n size=\"s\"\r\n icon=\"ri-delete-bin-line\"\r\n color=\"red\"\r\n type=\"ghost\"\r\n [iconOnly]=\"true\">\r\n </ui-button>\r\n </div>\r\n <div\r\n class=\"form-block-card-body\"\r\n cdkDropList\r\n [cdkDropListData]=\"block().fields\"\r\n (cdkDropListDropped)=\"reorderFields($event)\">\r\n \r\n <!-- FIELDS -->\r\n @for(edtField of block().fields; track edtField.code; let i = $index) {\r\n <div class=\"field-with-subfields\">\r\n <div\r\n class=\"xfield-card\"\r\n cdkDrag\r\n [class.selected-field]=\"isRepeaterParentSelected(edtField)\"\r\n (click)=\"selectField(edtField)\">\r\n <i class=\"ri-draggable field-drag-handle\" cdkDragHandle></i>\r\n\r\n <div class=\"xfield-card-icon\">\r\n <i [class]=\"edtField.field?.icon ?? ''\"></i> \r\n </div>\r\n <div class=\"xfield-card-body\">\r\n <b> {{edtField.fieldData.label}} \r\n @if(edtField.fieldData.visibilityRules) {\r\n <i class=\"branched-field ri-git-merge-line\"></i> \r\n <span class=\"vs-rule\"> {{edtField.fieldData.visibilityRules[0]?.fieldLabel ?? edtField.fieldData.visibilityRules[0]?.fieldName ?? '-'}} </span>\r\n }\r\n </b>\r\n <p> {{edtField.field?.label ?? edtField.type ?? edtField.fieldData.type}} </p>\r\n </div>\r\n <div class=\"xfield-card-actions\">\r\n @if (edtField.fieldData.type === 'repeater') {\r\n <ui-button\r\n [icon]=\"isRepeaterExpanded(edtField.code) ? 'ri-arrow-up-s-line' : 'ri-arrow-down-s-line'\"\r\n size=\"s\" color=\"black\" type=\"ghost\" [iconOnly]=\"true\"\r\n (click)=\"toggleRepeater(edtField.code, $event)\">\r\n </ui-button>\r\n }\r\n <ui-button\r\n [uiTooltip]=\"(isVisibilityParent(edtField.code) ? 'form_builder.delete_field_dependency_blocked' : 'form_builder.field_editor.delete') | uicTranslate\"\r\n [disabled]=\"isVisibilityParent(edtField.code)\"\r\n (click)=\"deleteField(edtField.code, $event)\"\r\n size=\"s\"\r\n icon=\"ri-delete-bin-line\"\r\n color=\"red\"\r\n type=\"ghost\"\r\n [iconOnly]=\"true\">\r\n </ui-button>\r\n </div>\r\n </div>\r\n\r\n @if (edtField.fieldData.type === 'repeater' && isRepeaterExpanded(edtField.code)) {\r\n <div class=\"repeater-inline-panel\">\r\n <div\r\n class=\"repeater-subfields-list\"\r\n cdkDropList\r\n [cdkDropListData]=\"edtField.repeaterSubFields ?? []\"\r\n (cdkDropListDropped)=\"reorderSubFields(edtField.code, $event)\">\r\n @for (sf of edtField.repeaterSubFields ?? []; track sf.code) {\r\n <div\r\n class=\"xfield-card subfield-card\"\r\n cdkDrag\r\n [class.selected-field]=\"selectedSubFieldCode === sf.code\"\r\n (click)=\"onSelectSubField(edtField.code, sf, $event)\">\r\n <i class=\"ri-draggable field-drag-handle\" cdkDragHandle></i>\r\n <div class=\"xfield-card-icon\">\r\n <i [class]=\"sf.field?.icon ?? 'ri-question-line'\"></i>\r\n </div>\r\n <div class=\"xfield-card-body\">\r\n <b>{{ sf.fieldData.label || sf.fieldData.name }}</b>\r\n <p>{{ sf.field?.label ?? sf.type ?? sf.fieldData.type }}</p>\r\n </div>\r\n <div class=\"xfield-card-actions\">\r\n <ui-button\r\n size=\"s\" icon=\"ri-delete-bin-line\" color=\"red\" type=\"ghost\" [iconOnly]=\"true\"\r\n (click)=\"deleteSubField(edtField.code, sf.code, $event)\">\r\n </ui-button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"empty-hint\">{{'form_builder.repeater.no_subfields' | uicTranslate}}</p>\r\n }\r\n </div>\r\n <div class=\"new-subfield\">\r\n {{'form_builder.repeater.add_subfield' | uicTranslate}}\r\n <lib-field-type-selector [excludeTypes]=\"excludeSubFieldTypes\" (selectType)=\"addSubField(edtField.code, $event)\"></lib-field-type-selector>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n }\r\n\r\n </div>\r\n <div class=\"new-field\">\r\n {{'form_builder.add_new_field' | uicTranslate}}\r\n <lib-field-type-selector (selectType)=\"addField($event)\"></lib-field-type-selector>\r\n </div>\r\n</div>\r\n", styles: [".form-block-card{border:solid 1px var(--blue-500);border-radius:5px;overflow:hidden}.form-block-card-header{display:flex;gap:5px;align-items:center;background-color:#fff;padding:10px;border-bottom:solid 1px var(--grey-300)}.form-block-card-header>div{flex:1 1;gap:4px;display:flex;flex-direction:column}.form-block-card-header>div input{border:solid 1px var(--grey-100);padding:3px 6px;border-radius:5px}.form-block-card-header>div input:focus{border:solid 1px var(--primary-500);outline:none}.form-block-card-body{padding:15px;display:flex;flex-direction:column;background-color:var(--grey-50);gap:10px}.xfield-card{background-color:#fff;border-radius:5px;padding:10px;gap:10px;display:flex;align-items:center;border:solid 2px var(--grey-200);cursor:pointer;transition:border-color .3s ease}.xfield-card:hover{border-color:var(--grey-300)}.xfield-card-icon{display:flex;justify-content:center;align-items:center;border-radius:5px;width:30px;height:30px;font-size:20px;background-color:var(--grey-300);color:var(--grey-600)}.xfield-card-body{flex:1 1}.xfield-card-body b{display:flex;align-items:center;gap:5px}.xfield-card-body p{color:var(--grey-400);font-size:12px}.field-drag-handle{cursor:grab;color:var(--grey-400);transition:color .2s ease}.field-drag-handle:active{cursor:grabbing}.xfield-card:hover .field-drag-handle{color:var(--grey-600)}.cdk-drag-preview{box-sizing:border-box;border-radius:5px;box-shadow:0 6px 12px #0000002e}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.field-with-subfields{display:flex;flex-direction:column;gap:0}.repeater-inline-panel{background-color:var(--grey-100);border:solid 1px var(--blue-300);border-top:none;border-radius:0 0 5px 5px;padding:10px 10px 8px 28px;display:flex;flex-direction:column;gap:8px}.repeater-subfields-list{display:flex;flex-direction:column;gap:6px;min-height:6px}.subfield-card{font-size:13px;border-radius:4px;padding:7px 8px!important}.subfield-card .xfield-card-icon{width:26px!important;height:26px!important;font-size:16px!important}.subfield-card b{font-size:13px}.subfield-card p{font-size:11px}.new-subfield{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--grey-500);flex-wrap:wrap;padding-top:2px}.empty-hint{font-size:12px;color:var(--grey-400);text-align:center;padding:4px 0;margin:0}.form-block-card-body.cdk-drop-list-dragging .xfield-card:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.branched-field{color:var(--yellow-600)}.selected-field,.selected-field:hover{border-color:var(--primary-500)}.new-field{border-top:solid 1px var(--grey-300);padding:10px;background-color:#fff;display:flex;flex-direction:column;gap:5px;font-size:14px}.vs-rule{font-size:12px;color:var(--grey-600)}\n"], dependencies: [{ kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "directive", type: UicToolTipDirective, selector: "[uiTooltip]", inputs: ["uiTooltip"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1$3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i1$3.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FieldTypeSelectorComponent, selector: "lib-field-type-selector", inputs: ["excludeTypes"], outputs: ["selectType"] }] });
9289
9914
  }
9290
9915
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: BlockEditorComponent, decorators: [{
9291
9916
  type: Component,
@@ -9296,9 +9921,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
9296
9921
  DragDropModule,
9297
9922
  FormsModule,
9298
9923
  FieldTypeSelectorComponent
9299
- ], template: "<div class=\"form-block-card\">\n <div class=\"form-block-card-header\">\n <div>\n <input [placeholder]=\"'form_builder.block_title' | uicTranslate\" [ngModel]=\"block().title\" (ngModelChange)=\"changeTitle($event)\">\n <input [placeholder]=\"'form_builder.block_subtitle' | uicTranslate\" [ngModel]=\"block().subtitle\" (ngModelChange)=\"changeSubtitle($event)\">\n </div>\n <ui-button [uiTooltip]=\"'form_builder.delete_block' | uicTranslate\" (click)=\"deleteBlock.emit(block().code)\" size=\"s\" icon=\"ri-delete-bin-line\" color=\"red\" type=\"ghost\" [iconOnly]=\"true\"></ui-button>\n </div>\n <div\n class=\"form-block-card-body\"\n cdkDropList\n [cdkDropListData]=\"block().fields\"\n (cdkDropListDropped)=\"reorderFields($event)\">\n \n <!-- FIELDS -->\n @for(edtField of block().fields; track edtField.code; let i = $index) {\n <div\n class=\"xfield-card\"\n cdkDrag\n [class.selected-field]=\"selectedFieldId === edtField.code\"\n (click)=\"selectField(edtField)\">\n <i class=\"ri-draggable field-drag-handle\" cdkDragHandle></i>\n\n <div class=\"xfield-card-icon\">\n <i [class]=\"edtField.field?.icon ?? ''\"></i> \n </div>\n <div class=\"xfield-card-body\">\n <b> {{edtField.fieldData.label}} \n @if(edtField.fieldData.visibilityRules) {\n <i class=\"branched-field ri-git-merge-line\"></i> \n <span class=\"vs-rule\"> {{edtField.fieldData.visibilityRules[0]?.fieldLabel ?? edtField.fieldData.visibilityRules[0]?.fieldName ?? '-'}} </span>\n }\n </b>\n <p> {{edtField.field?.label ?? edtField.type ?? edtField.fieldData.type}} </p>\n </div>\n <div class=\"xfield-card-actions\">\n <ui-button (click)=\"deleteField(edtField.code, $event)\" size=\"s\" icon=\"ri-delete-bin-line\" color=\"red\" type=\"ghost\" [iconOnly]=\"true\"></ui-button>\n </div>\n </div>\n\n }\n\n </div>\n <div class=\"new-field\">\n {{'form_builder.add_new_field' | uicTranslate}}\n <lib-field-type-selector (selectType)=\"addField($event)\"></lib-field-type-selector>\n </div>\n</div>\n", styles: [".form-block-card{border:solid 1px var(--blue-500);border-radius:5px;overflow:hidden}.form-block-card-header{display:flex;gap:5px;align-items:center;background-color:#fff;padding:10px;border-bottom:solid 1px var(--grey-300)}.form-block-card-header>div{flex:1 1;gap:4px;display:flex;flex-direction:column}.form-block-card-header>div input{border:solid 1px var(--grey-100);padding:3px 6px;border-radius:5px}.form-block-card-header>div input:focus{border:solid 1px var(--primary-500);outline:none}.form-block-card-body{padding:15px;display:flex;flex-direction:column;background-color:var(--grey-50);gap:10px}.xfield-card{background-color:#fff;border-radius:5px;padding:10px;gap:10px;display:flex;align-items:center;border:solid 2px var(--grey-200);cursor:pointer;transition:border-color .3s ease}.xfield-card:hover{border-color:var(--grey-300)}.xfield-card-icon{display:flex;justify-content:center;align-items:center;border-radius:5px;width:30px;height:30px;font-size:20px;background-color:var(--grey-300);color:var(--grey-600)}.xfield-card-body{flex:1 1}.xfield-card-body b{display:flex;align-items:center;gap:5px}.xfield-card-body p{color:var(--grey-400);font-size:12px}.field-drag-handle{cursor:grab;color:var(--grey-400);transition:color .2s ease}.field-drag-handle:active{cursor:grabbing}.xfield-card:hover .field-drag-handle{color:var(--grey-600)}.cdk-drag-preview{box-sizing:border-box;border-radius:5px;box-shadow:0 6px 12px #0000002e}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.form-block-card-body.cdk-drop-list-dragging .xfield-card:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.branched-field{color:var(--yellow-600)}.selected-field,.selected-field:hover{border-color:var(--primary-500)}.new-field{border-top:solid 1px var(--grey-300);padding:10px;background-color:#fff;display:flex;flex-direction:column;gap:5px;font-size:14px}.vs-rule{font-size:12px;color:var(--grey-600)}\n"] }]
9924
+ ], template: "<div class=\"form-block-card\">\r\n <div class=\"form-block-card-header\">\r\n <div>\r\n <input [placeholder]=\"'form_builder.block_title' | uicTranslate\" [ngModel]=\"block().title\" (ngModelChange)=\"changeTitle($event)\">\r\n <input [placeholder]=\"'form_builder.block_subtitle' | uicTranslate\" [ngModel]=\"block().subtitle\" (ngModelChange)=\"changeSubtitle($event)\">\r\n </div>\r\n <ui-button\r\n [uiTooltip]=\"(isBlockVisibilityParent() ? 'form_builder.delete_block_dependency_blocked' : 'form_builder.delete_block') | uicTranslate\"\r\n [disabled]=\"isBlockVisibilityParent()\"\r\n (click)=\"requestDeleteBlock($event)\"\r\n size=\"s\"\r\n icon=\"ri-delete-bin-line\"\r\n color=\"red\"\r\n type=\"ghost\"\r\n [iconOnly]=\"true\">\r\n </ui-button>\r\n </div>\r\n <div\r\n class=\"form-block-card-body\"\r\n cdkDropList\r\n [cdkDropListData]=\"block().fields\"\r\n (cdkDropListDropped)=\"reorderFields($event)\">\r\n \r\n <!-- FIELDS -->\r\n @for(edtField of block().fields; track edtField.code; let i = $index) {\r\n <div class=\"field-with-subfields\">\r\n <div\r\n class=\"xfield-card\"\r\n cdkDrag\r\n [class.selected-field]=\"isRepeaterParentSelected(edtField)\"\r\n (click)=\"selectField(edtField)\">\r\n <i class=\"ri-draggable field-drag-handle\" cdkDragHandle></i>\r\n\r\n <div class=\"xfield-card-icon\">\r\n <i [class]=\"edtField.field?.icon ?? ''\"></i> \r\n </div>\r\n <div class=\"xfield-card-body\">\r\n <b> {{edtField.fieldData.label}} \r\n @if(edtField.fieldData.visibilityRules) {\r\n <i class=\"branched-field ri-git-merge-line\"></i> \r\n <span class=\"vs-rule\"> {{edtField.fieldData.visibilityRules[0]?.fieldLabel ?? edtField.fieldData.visibilityRules[0]?.fieldName ?? '-'}} </span>\r\n }\r\n </b>\r\n <p> {{edtField.field?.label ?? edtField.type ?? edtField.fieldData.type}} </p>\r\n </div>\r\n <div class=\"xfield-card-actions\">\r\n @if (edtField.fieldData.type === 'repeater') {\r\n <ui-button\r\n [icon]=\"isRepeaterExpanded(edtField.code) ? 'ri-arrow-up-s-line' : 'ri-arrow-down-s-line'\"\r\n size=\"s\" color=\"black\" type=\"ghost\" [iconOnly]=\"true\"\r\n (click)=\"toggleRepeater(edtField.code, $event)\">\r\n </ui-button>\r\n }\r\n <ui-button\r\n [uiTooltip]=\"(isVisibilityParent(edtField.code) ? 'form_builder.delete_field_dependency_blocked' : 'form_builder.field_editor.delete') | uicTranslate\"\r\n [disabled]=\"isVisibilityParent(edtField.code)\"\r\n (click)=\"deleteField(edtField.code, $event)\"\r\n size=\"s\"\r\n icon=\"ri-delete-bin-line\"\r\n color=\"red\"\r\n type=\"ghost\"\r\n [iconOnly]=\"true\">\r\n </ui-button>\r\n </div>\r\n </div>\r\n\r\n @if (edtField.fieldData.type === 'repeater' && isRepeaterExpanded(edtField.code)) {\r\n <div class=\"repeater-inline-panel\">\r\n <div\r\n class=\"repeater-subfields-list\"\r\n cdkDropList\r\n [cdkDropListData]=\"edtField.repeaterSubFields ?? []\"\r\n (cdkDropListDropped)=\"reorderSubFields(edtField.code, $event)\">\r\n @for (sf of edtField.repeaterSubFields ?? []; track sf.code) {\r\n <div\r\n class=\"xfield-card subfield-card\"\r\n cdkDrag\r\n [class.selected-field]=\"selectedSubFieldCode === sf.code\"\r\n (click)=\"onSelectSubField(edtField.code, sf, $event)\">\r\n <i class=\"ri-draggable field-drag-handle\" cdkDragHandle></i>\r\n <div class=\"xfield-card-icon\">\r\n <i [class]=\"sf.field?.icon ?? 'ri-question-line'\"></i>\r\n </div>\r\n <div class=\"xfield-card-body\">\r\n <b>{{ sf.fieldData.label || sf.fieldData.name }}</b>\r\n <p>{{ sf.field?.label ?? sf.type ?? sf.fieldData.type }}</p>\r\n </div>\r\n <div class=\"xfield-card-actions\">\r\n <ui-button\r\n size=\"s\" icon=\"ri-delete-bin-line\" color=\"red\" type=\"ghost\" [iconOnly]=\"true\"\r\n (click)=\"deleteSubField(edtField.code, sf.code, $event)\">\r\n </ui-button>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"empty-hint\">{{'form_builder.repeater.no_subfields' | uicTranslate}}</p>\r\n }\r\n </div>\r\n <div class=\"new-subfield\">\r\n {{'form_builder.repeater.add_subfield' | uicTranslate}}\r\n <lib-field-type-selector [excludeTypes]=\"excludeSubFieldTypes\" (selectType)=\"addSubField(edtField.code, $event)\"></lib-field-type-selector>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n }\r\n\r\n </div>\r\n <div class=\"new-field\">\r\n {{'form_builder.add_new_field' | uicTranslate}}\r\n <lib-field-type-selector (selectType)=\"addField($event)\"></lib-field-type-selector>\r\n </div>\r\n</div>\r\n", styles: [".form-block-card{border:solid 1px var(--blue-500);border-radius:5px;overflow:hidden}.form-block-card-header{display:flex;gap:5px;align-items:center;background-color:#fff;padding:10px;border-bottom:solid 1px var(--grey-300)}.form-block-card-header>div{flex:1 1;gap:4px;display:flex;flex-direction:column}.form-block-card-header>div input{border:solid 1px var(--grey-100);padding:3px 6px;border-radius:5px}.form-block-card-header>div input:focus{border:solid 1px var(--primary-500);outline:none}.form-block-card-body{padding:15px;display:flex;flex-direction:column;background-color:var(--grey-50);gap:10px}.xfield-card{background-color:#fff;border-radius:5px;padding:10px;gap:10px;display:flex;align-items:center;border:solid 2px var(--grey-200);cursor:pointer;transition:border-color .3s ease}.xfield-card:hover{border-color:var(--grey-300)}.xfield-card-icon{display:flex;justify-content:center;align-items:center;border-radius:5px;width:30px;height:30px;font-size:20px;background-color:var(--grey-300);color:var(--grey-600)}.xfield-card-body{flex:1 1}.xfield-card-body b{display:flex;align-items:center;gap:5px}.xfield-card-body p{color:var(--grey-400);font-size:12px}.field-drag-handle{cursor:grab;color:var(--grey-400);transition:color .2s ease}.field-drag-handle:active{cursor:grabbing}.xfield-card:hover .field-drag-handle{color:var(--grey-600)}.cdk-drag-preview{box-sizing:border-box;border-radius:5px;box-shadow:0 6px 12px #0000002e}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.field-with-subfields{display:flex;flex-direction:column;gap:0}.repeater-inline-panel{background-color:var(--grey-100);border:solid 1px var(--blue-300);border-top:none;border-radius:0 0 5px 5px;padding:10px 10px 8px 28px;display:flex;flex-direction:column;gap:8px}.repeater-subfields-list{display:flex;flex-direction:column;gap:6px;min-height:6px}.subfield-card{font-size:13px;border-radius:4px;padding:7px 8px!important}.subfield-card .xfield-card-icon{width:26px!important;height:26px!important;font-size:16px!important}.subfield-card b{font-size:13px}.subfield-card p{font-size:11px}.new-subfield{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--grey-500);flex-wrap:wrap;padding-top:2px}.empty-hint{font-size:12px;color:var(--grey-400);text-align:center;padding:4px 0;margin:0}.form-block-card-body.cdk-drop-list-dragging .xfield-card:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.branched-field{color:var(--yellow-600)}.selected-field,.selected-field:hover{border-color:var(--primary-500)}.new-field{border-top:solid 1px var(--grey-300);padding:10px;background-color:#fff;display:flex;flex-direction:column;gap:5px;font-size:14px}.vs-rule{font-size:12px;color:var(--grey-600)}\n"] }]
9300
9925
  }], propDecorators: { selectedFieldId: [{
9301
9926
  type: Input
9927
+ }], selectedSubFieldCode: [{
9928
+ type: Input
9929
+ }], visibilityParentFieldCodes: [{
9930
+ type: Input
9302
9931
  }] } });
9303
9932
 
9304
9933
  class UicUserFormbuilderComponent {
@@ -9319,9 +9948,15 @@ class UicUserFormbuilderComponent {
9319
9948
  editingSnapshot = signal(null);
9320
9949
  isReadOnly = signal(true);
9321
9950
  selectedField = signal(null);
9951
+ editingSubField = signal(null);
9322
9952
  focusNewFieldCode = signal(null);
9323
9953
  propertiesWidth = signal(250);
9324
9954
  previewSchema = computed(() => helperShowFormFromBuilder(this.editableBlocks(), this.editableCols()));
9955
+ visibilityParentFieldCodes = computed(() => new Set(this.editableBlocks()
9956
+ .flatMap(block => block.fields)
9957
+ .flatMap(field => field.fieldData.visibilityRules ?? [])
9958
+ .map(rule => rule.fieldName)
9959
+ .filter((fieldName) => !!fieldName)));
9325
9960
  isResizingProperties = false;
9326
9961
  resizeStartX = 0;
9327
9962
  resizeStartWidth = 0;
@@ -9332,7 +9967,8 @@ class UicUserFormbuilderComponent {
9332
9967
  .filter(field => field.code !== currentSelected?.code)
9333
9968
  .map(field => ({
9334
9969
  id: field.code,
9335
- text: field.fieldData.label || field.fieldData.name
9970
+ text: field.fieldData.label || field.fieldData.name,
9971
+ fieldType: field.fieldData.type
9336
9972
  }));
9337
9973
  return {
9338
9974
  fieldName: fieldOptions,
@@ -9377,6 +10013,18 @@ class UicUserFormbuilderComponent {
9377
10013
  if (selectedField !== this.selectedField()) {
9378
10014
  this.selectedField.set(selectedField);
9379
10015
  }
10016
+ // Sync editingSubField
10017
+ const subCtx = this.editingSubField();
10018
+ if (subCtx) {
10019
+ const parentField = blocks.flatMap(b => b.fields).find(f => f.code === subCtx.parentCode);
10020
+ const latestSf = parentField?.repeaterSubFields?.find(sf => sf.code === subCtx.subField.code);
10021
+ if (!latestSf) {
10022
+ this.editingSubField.set(null);
10023
+ }
10024
+ else if (latestSf !== subCtx.subField) {
10025
+ this.editingSubField.set({ ...subCtx, subField: latestSf });
10026
+ }
10027
+ }
9380
10028
  });
9381
10029
  }
9382
10030
  addBlock() {
@@ -9419,6 +10067,38 @@ class UicUserFormbuilderComponent {
9419
10067
  selectField(field) {
9420
10068
  this.selectedField.set(field);
9421
10069
  this.focusNewFieldCode.set(null);
10070
+ this.editingSubField.set(null);
10071
+ }
10072
+ onSelectSubField(event) {
10073
+ this.editingSubField.set(event);
10074
+ if (event) {
10075
+ // Highlight the parent repeater field in the block (deselect root selection)
10076
+ const parentField = this.editableBlocks()
10077
+ .flatMap(b => b.fields)
10078
+ .find(f => f.code === event.parentCode);
10079
+ if (parentField)
10080
+ this.selectedField.set(parentField);
10081
+ }
10082
+ }
10083
+ onSubFieldChange(updatedFieldData) {
10084
+ const context = this.editingSubField();
10085
+ if (!context)
10086
+ return;
10087
+ const updatedSubField = {
10088
+ ...context.subField,
10089
+ type: updatedFieldData.type,
10090
+ fieldData: updatedFieldData
10091
+ };
10092
+ this.editingSubField.set({ ...context, subField: updatedSubField });
10093
+ this.editableBlocks.update(blocks => blocks.map(block => ({
10094
+ ...block,
10095
+ fields: block.fields.map(field => {
10096
+ if (field.code !== context.parentCode)
10097
+ return field;
10098
+ const updatedSubFields = (field.repeaterSubFields ?? []).map(sf => sf.code !== context.subField.code ? sf : updatedSubField);
10099
+ return { ...field, repeaterSubFields: updatedSubFields };
10100
+ })
10101
+ })));
9422
10102
  }
9423
10103
  startPropertiesResize(event) {
9424
10104
  event.preventDefault();
@@ -9467,7 +10147,10 @@ class UicUserFormbuilderComponent {
9467
10147
  cols: this.editableCols(),
9468
10148
  blocks: blocksWithOrderedFields.map(block => ({
9469
10149
  ...block,
9470
- fields: block.fields.map(({ field, ...restField }) => restField)
10150
+ fields: block.fields.map(({ field, ...restField }) => ({
10151
+ ...restField,
10152
+ repeaterSubFields: restField.repeaterSubFields?.map(({ field: _sf, ...sfRest }) => sfRest)
10153
+ }))
9471
10154
  }))
9472
10155
  });
9473
10156
  }
@@ -9568,6 +10251,22 @@ class UicUserFormbuilderComponent {
9568
10251
  };
9569
10252
  }
9570
10253
  }
10254
+ // Hydrate sub-fields for repeater
10255
+ if (hydratedField.repeaterSubFields?.length) {
10256
+ const hydratedSubFields = hydratedField.repeaterSubFields.map(sf => {
10257
+ if (sf.field)
10258
+ return sf;
10259
+ const sfConfig = FIELDS_CONFIG.find(c => c.value === sf.type);
10260
+ if (!sfConfig)
10261
+ return sf;
10262
+ changed = true;
10263
+ blockChanged = true;
10264
+ return { ...sf, field: sfConfig };
10265
+ });
10266
+ if (hydratedSubFields !== hydratedField.repeaterSubFields) {
10267
+ hydratedField = { ...hydratedField, repeaterSubFields: hydratedSubFields };
10268
+ }
10269
+ }
9571
10270
  const visibilityRules = hydratedField.fieldData.visibilityRules;
9572
10271
  if (!visibilityRules?.length)
9573
10272
  return hydratedField;
@@ -9641,7 +10340,7 @@ class UicUserFormbuilderComponent {
9641
10340
  return value;
9642
10341
  }
9643
10342
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicUserFormbuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9644
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicUserFormbuilderComponent, isStandalone: true, selector: "ui-user-formbuilder", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, formTitle: { classPropertyName: "formTitle", publicName: "formTitle", isSignal: false, isRequired: false, transformFunction: null }, optionSources: { classPropertyName: "optionSources", publicName: "optionSources", isSignal: false, isRequired: false, transformFunction: null }, selectOptionsResolver: { classPropertyName: "selectOptionsResolver", publicName: "selectOptionsResolver", isSignal: false, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, editableFormInput: { classPropertyName: "editableFormInput", publicName: "editableForm", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitFormRequest: "submitFormRequest" }, host: { listeners: { "document:mousemove": "onPropertiesResize($event)", "document:mouseup": "stopPropertiesResize()" } }, ngImport: i0, template: "<div class=\"formeditor\" [class.focused]=\"!isReadOnly()\">\r\n <div class=\"formeditor-header\">\r\n <div style=\"flex: 1 1;\">\r\n {{formTitle}} \r\n \r\n <div class=\"cols-selector\"> \r\n @if (isReadOnly()) {\r\n {{editableCols()}}\r\n }@else {\r\n <ui-select [options]=\"[{id: 1, text: '1'}, {id: 2, text: '2'}, {id: 3, text: '3'}, {id: 4, text: '4'}]\" [ngModel]=\"editableCols()\" (ngModelChange)=\"editableCols.set($event)\"></ui-select> \r\n }\r\n col(s)</div>\r\n </div>\r\n @if (isReadOnly()) {\r\n @if (!disabled) {\r\n <ui-button color=\"black\" icon=\"ri-edit-line\" text=\"Editar\" (click)=\"enableEditMode()\"></ui-button>\r\n }\r\n } @else {\r\n <ui-button color=\"black\" type=\"bordered\" icon=\"ri-eye-line\" [text]=\"'form_builder.preview_form' | uicTranslate\" (click)=\"printForm()\"></ui-button>\r\n <ui-button color=\"black\" type=\"bordered\" icon=\"ri-close-line\" text=\"Descartar cambios\" (click)=\"discardChanges()\"></ui-button>\r\n <ui-button color=\"black\" icon=\"ri-check-line\" [text]=\"'form_builder.submit_form' | uicTranslate\" (click)=\"submitForm()\"></ui-button>\r\n }\r\n </div>\r\n @if (!isReadOnly()) {\r\n <div class=\"formeditor-body\">\r\n \r\n <!-- BLOCKS -->\r\n <div class=\"formeditor-overflow\">\r\n <div class=\"formeditor-workarea\">\r\n @for (block of editableBlocks(); track block.code; let i = $index) {\r\n <lib-block-editor \r\n [block]=\"block\"\r\n [selectedFieldId]=\"selectedField()?.code ?? null\"\n (blockChange)=\"onBlockChange(i, $event)\"\n (addFieldRequest)=\"addField(block.code, $event)\"\n (notifySelectedField)=\"selectField($event)\"\n (deleteBlock)=\"deleteBlock($event)\">\n </lib-block-editor>\n }\r\n <ui-button type=\"bordered\" icon=\"ri-add-line\" color=\"black\" [text]=\"'form_builder.add_block' | uicTranslate\" (click)=\"addBlock()\"></ui-button>\r\n </div>\r\n </div>\r\n <!-- PROPERTIES -->\r\n <div class=\"formeditor-properties\" [style.width.px]=\"propertiesWidth()\">\n <div\n class=\"formeditor-properties-resize\"\n (mousedown)=\"startPropertiesResize($event)\">\n </div>\n <h3>Propiedades</h3>\n <div class=\"formeditor-properties-form\">\n @if (selectedField() ) {\r\n <lib-field-editor \n [config]=\"selectedField()!\"\n [focusRequiredField]=\"focusNewFieldCode() === selectedField()?.code\"\n [options]=\"dependencyOptions()\"\n (fieldChange)=\"onFieldChange($event)\">\n </lib-field-editor>\n }@else{\r\n <div class=\"no-selected-field\">\r\n <i class=\"ri-edit-box-line\"></i>\r\n <p>{{'form_builder.select_field_to_edit' | uicTranslate}}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }@else {\r\n <div class=\"form-preview\">\r\n <ui-form-wrapper \n [schema]=\"previewSchema()\"\n [selectOptionsResolver]=\"selectOptionsResolver\"\n [showButtons]=\"false\">\n </ui-form-wrapper>\n </div>\r\n }\r\n </div>\r\n\r\n", styles: [".formeditor{display:flex;flex-direction:column;overflow:hidden;background-color:var(--grey-100);border:solid 1px var(--grey-300);border-radius:5px;max-height:500px}.formeditor-header{background-color:#fff;padding:5px 10px;align-items:center;display:flex;gap:10px;border-bottom:solid 1px var(--grey-300)}.formeditor-body{display:flex;gap:10px;overflow:hidden;padding:10px;min-height:0}.formeditor-overflow{padding:5px;flex:1 1;overflow:auto;min-width:0}.formeditor-workarea{display:flex;flex-direction:column;gap:15px;height:fit-content}.formeditor-properties{width:250px;flex:0 0 auto;display:flex;flex-direction:column;position:relative;background-color:#fff;border:solid 1px var(--grey-300);border-radius:5px;min-height:0;min-width:250px;max-width:600px}.formeditor-properties-resize{position:absolute;top:0;bottom:0;left:-6px;width:10px;cursor:col-resize;z-index:1}.formeditor-properties>h3{padding:10px;border-bottom:solid 1px var(--grey-300)}.formeditor-properties-form{padding:10px;overflow:auto;flex:1 1 auto;min-height:0}.form-preview{padding:20px;overflow:auto;background-color:#fff}.focused{border:solid 1px var(--primary-400);border-radius:10px;box-shadow:0 0 0 3px var(--secondary-alpha)}.no-selected-field{width:100%;display:flex;flex-direction:column;justify-content:center;align-items:center;padding:20px 0;font-size:13px;gap:15px;text-align:center;color:var(--grey-400)}.no-selected-field i{font-size:24px}.cols-selector{margin-left:8px;padding-left:8px;border-left:solid 1px var(--grey-400);display:inline-flex;align-items:center;gap:5px}\n"], dependencies: [{ kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "component", type: UicFormWrapperComponent, selector: "ui-form-wrapper", inputs: ["schema", "fields", "cols", "externalData", "selectOptionsResolver", "loading", "disabled", "showButtons", "fillSelects", "initialValues", "focusFieldName", "focusFieldTrigger", "fileUidResolverFn"], outputs: ["formSubmit", "formChange", "optionsSourceError"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }, { kind: "component", type: FieldEditorComponent, selector: "lib-field-editor", inputs: ["config", "focusRequiredField", "options"], outputs: ["fieldChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: BlockEditorComponent, selector: "lib-block-editor", inputs: ["block", "selectedFieldId"], outputs: ["blockChange", "addFieldRequest", "deleteBlock", "notifySelectedField"] }, { kind: "component", type: UicSelectComponent, selector: "ui-select", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "showSubtitle", "disabled", "nonSelectedText", "noneText", "emptyText", "searcherEnabled", "loading", "nullable", "options"] }] });
10343
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: UicUserFormbuilderComponent, isStandalone: true, selector: "ui-user-formbuilder", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, formTitle: { classPropertyName: "formTitle", publicName: "formTitle", isSignal: false, isRequired: false, transformFunction: null }, optionSources: { classPropertyName: "optionSources", publicName: "optionSources", isSignal: false, isRequired: false, transformFunction: null }, selectOptionsResolver: { classPropertyName: "selectOptionsResolver", publicName: "selectOptionsResolver", isSignal: false, isRequired: false, transformFunction: null }, readOnly: { classPropertyName: "readOnly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, editableFormInput: { classPropertyName: "editableFormInput", publicName: "editableForm", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitFormRequest: "submitFormRequest" }, host: { listeners: { "document:mousemove": "onPropertiesResize($event)", "document:mouseup": "stopPropertiesResize()" } }, ngImport: i0, template: "<div class=\"formeditor\" [class.focused]=\"!isReadOnly()\">\r\n <div class=\"formeditor-header\">\r\n <div style=\"flex: 1 1;\">\r\n {{formTitle}} \r\n \r\n <div class=\"cols-selector\"> \r\n @if (isReadOnly()) {\r\n {{editableCols()}}\r\n }@else {\r\n <ui-select [options]=\"[{id: 1, text: '1'}, {id: 2, text: '2'}, {id: 3, text: '3'}, {id: 4, text: '4'}]\" [ngModel]=\"editableCols()\" (ngModelChange)=\"editableCols.set($event)\"></ui-select> \r\n }\r\n col(s)</div>\r\n </div>\r\n @if (isReadOnly()) {\r\n @if (!disabled) {\r\n <ui-button color=\"black\" icon=\"ri-edit-line\" text=\"Editar\" (click)=\"enableEditMode()\"></ui-button>\r\n }\r\n } @else {\r\n <ui-button color=\"black\" type=\"bordered\" icon=\"ri-eye-line\" [text]=\"'form_builder.preview_form' | uicTranslate\" (click)=\"printForm()\"></ui-button>\r\n <ui-button color=\"black\" type=\"bordered\" icon=\"ri-close-line\" text=\"Descartar cambios\" (click)=\"discardChanges()\"></ui-button>\r\n <ui-button color=\"black\" icon=\"ri-check-line\" [text]=\"'form_builder.submit_form' | uicTranslate\" (click)=\"submitForm()\"></ui-button>\r\n }\r\n </div>\r\n @if (!isReadOnly()) {\r\n <div class=\"formeditor-body\">\r\n \r\n <!-- BLOCKS -->\r\n <div class=\"formeditor-overflow\">\r\n <div class=\"formeditor-workarea\">\r\n @for (block of editableBlocks(); track block.code; let i = $index) {\r\n <lib-block-editor \r\n [block]=\"block\"\r\n [selectedFieldId]=\"selectedField()?.code ?? null\"\r\n [selectedSubFieldCode]=\"editingSubField()?.subField?.code ?? null\"\r\n [visibilityParentFieldCodes]=\"visibilityParentFieldCodes()\"\r\n (blockChange)=\"onBlockChange(i, $event)\"\r\n (addFieldRequest)=\"addField(block.code, $event)\"\r\n (notifySelectedField)=\"selectField($event)\"\r\n (notifySelectedSubField)=\"onSelectSubField($event)\"\r\n (deleteBlock)=\"deleteBlock($event)\">\r\n </lib-block-editor>\r\n }\r\n <ui-button type=\"bordered\" icon=\"ri-add-line\" color=\"black\" [text]=\"'form_builder.add_block' | uicTranslate\" (click)=\"addBlock()\"></ui-button>\r\n </div>\r\n </div>\r\n <!-- PROPERTIES -->\r\n <div class=\"formeditor-properties\" [style.width.px]=\"propertiesWidth()\">\r\n <div\r\n class=\"formeditor-properties-resize\"\r\n (mousedown)=\"startPropertiesResize($event)\">\r\n </div>\r\n <h3>Propiedades</h3>\r\n <div class=\"formeditor-properties-form\">\r\n @if (editingSubField()) {\r\n <div class=\"subfield-back-header\">\r\n <i class=\"ri-stack-line\"></i>\r\n <span>{{editingSubField()!.subField.fieldData.label || editingSubField()!.subField.fieldData.name}}</span>\r\n </div>\r\n <lib-field-editor\r\n [config]=\"editingSubField()!.subField\"\r\n [isSubField]=\"true\"\r\n [options]=\"{}\"\r\n (fieldChange)=\"onSubFieldChange($event)\">\r\n </lib-field-editor>\r\n } @else if (selectedField()) {\r\n <lib-field-editor \r\n [config]=\"selectedField()!\"\r\n [focusRequiredField]=\"focusNewFieldCode() === selectedField()?.code\"\r\n [options]=\"dependencyOptions()\"\r\n (fieldChange)=\"onFieldChange($event)\">\r\n </lib-field-editor>\r\n }@else{\r\n <div class=\"no-selected-field\">\r\n <i class=\"ri-edit-box-line\"></i>\r\n <p>{{'form_builder.select_field_to_edit' | uicTranslate}}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }@else {\r\n <div class=\"form-preview\">\r\n <ui-form-wrapper \r\n [schema]=\"previewSchema()\"\r\n [selectOptionsResolver]=\"selectOptionsResolver\"\r\n [showButtons]=\"false\">\r\n </ui-form-wrapper>\r\n </div>\r\n }\r\n </div>\r\n\r\n", styles: [".formeditor{display:flex;flex-direction:column;overflow:hidden;background-color:var(--grey-100);border:solid 1px var(--grey-300);border-radius:5px;max-height:500px}.formeditor-header{background-color:#fff;padding:5px 10px;align-items:center;display:flex;gap:10px;border-bottom:solid 1px var(--grey-300)}.formeditor-body{display:flex;gap:10px;overflow:hidden;padding:10px;min-height:0}.formeditor-overflow{padding:5px;flex:1 1;overflow:auto;min-width:0}.formeditor-workarea{display:flex;flex-direction:column;gap:15px;height:fit-content}.formeditor-properties{width:250px;flex:0 0 auto;display:flex;flex-direction:column;position:relative;background-color:#fff;border:solid 1px var(--grey-300);border-radius:5px;min-height:0;min-width:250px;max-width:600px}.formeditor-properties-resize{position:absolute;top:0;bottom:0;left:-6px;width:10px;cursor:col-resize;z-index:1}.formeditor-properties>h3{padding:10px;border-bottom:solid 1px var(--grey-300)}.formeditor-properties-form{padding:10px;overflow:auto;flex:1 1 auto;min-height:0}.form-preview{padding:20px;overflow:auto;background-color:#fff}.focused{border:solid 1px var(--primary-400);border-radius:10px;box-shadow:0 0 0 3px var(--secondary-alpha)}.no-selected-field{width:100%;display:flex;flex-direction:column;justify-content:center;align-items:center;padding:20px 0;font-size:13px;gap:15px;text-align:center;color:var(--grey-400)}.no-selected-field i{font-size:24px}.cols-selector{margin-left:8px;padding-left:8px;border-left:solid 1px var(--grey-400);display:inline-flex;align-items:center;gap:5px}.subfield-back-header{display:flex;align-items:center;gap:6px;padding:0 0 10px;border-bottom:solid 1px var(--grey-200);margin-bottom:10px;color:var(--grey-500);font-size:12px}.subfield-back-header i{font-size:14px;color:var(--primary-500)}.subfield-back-header span{font-weight:600;color:var(--grey-700);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "component", type: UicButtonComponent, selector: "ui-button", inputs: ["text", "icon", "rightIcon", "iconOnly", "disabled", "loading", "size", "type", "color"] }, { kind: "component", type: UicFormWrapperComponent, selector: "ui-form-wrapper", inputs: ["schema", "fields", "cols", "externalData", "selectOptionsResolver", "loading", "disabled", "showButtons", "fillSelects", "initialValues", "focusFieldName", "focusFieldTrigger", "fileUidResolverFn"], outputs: ["formSubmit", "formChange", "optionsSourceError"] }, { kind: "pipe", type: UicTranslatePipe, name: "uicTranslate" }, { kind: "component", type: FieldEditorComponent, selector: "lib-field-editor", inputs: ["config", "focusRequiredField", "isSubField", "options"], outputs: ["fieldChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: BlockEditorComponent, selector: "lib-block-editor", inputs: ["block", "selectedFieldId", "selectedSubFieldCode", "visibilityParentFieldCodes"], outputs: ["blockChange", "addFieldRequest", "deleteBlock", "notifySelectedField", "notifySelectedSubField"] }, { kind: "component", type: UicSelectComponent, selector: "ui-select", inputs: ["icon", "iconColor", "internalIcon", "internalIconColor", "size", "label", "error", "tip", "showSubtitle", "disabled", "nonSelectedText", "noneText", "emptyText", "searcherEnabled", "loading", "nullable", "options"] }] });
9645
10344
  }
9646
10345
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: UicUserFormbuilderComponent, decorators: [{
9647
10346
  type: Component,
@@ -9653,7 +10352,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
9653
10352
  FormsModule,
9654
10353
  BlockEditorComponent,
9655
10354
  UicSelectComponent
9656
- ], template: "<div class=\"formeditor\" [class.focused]=\"!isReadOnly()\">\r\n <div class=\"formeditor-header\">\r\n <div style=\"flex: 1 1;\">\r\n {{formTitle}} \r\n \r\n <div class=\"cols-selector\"> \r\n @if (isReadOnly()) {\r\n {{editableCols()}}\r\n }@else {\r\n <ui-select [options]=\"[{id: 1, text: '1'}, {id: 2, text: '2'}, {id: 3, text: '3'}, {id: 4, text: '4'}]\" [ngModel]=\"editableCols()\" (ngModelChange)=\"editableCols.set($event)\"></ui-select> \r\n }\r\n col(s)</div>\r\n </div>\r\n @if (isReadOnly()) {\r\n @if (!disabled) {\r\n <ui-button color=\"black\" icon=\"ri-edit-line\" text=\"Editar\" (click)=\"enableEditMode()\"></ui-button>\r\n }\r\n } @else {\r\n <ui-button color=\"black\" type=\"bordered\" icon=\"ri-eye-line\" [text]=\"'form_builder.preview_form' | uicTranslate\" (click)=\"printForm()\"></ui-button>\r\n <ui-button color=\"black\" type=\"bordered\" icon=\"ri-close-line\" text=\"Descartar cambios\" (click)=\"discardChanges()\"></ui-button>\r\n <ui-button color=\"black\" icon=\"ri-check-line\" [text]=\"'form_builder.submit_form' | uicTranslate\" (click)=\"submitForm()\"></ui-button>\r\n }\r\n </div>\r\n @if (!isReadOnly()) {\r\n <div class=\"formeditor-body\">\r\n \r\n <!-- BLOCKS -->\r\n <div class=\"formeditor-overflow\">\r\n <div class=\"formeditor-workarea\">\r\n @for (block of editableBlocks(); track block.code; let i = $index) {\r\n <lib-block-editor \r\n [block]=\"block\"\r\n [selectedFieldId]=\"selectedField()?.code ?? null\"\n (blockChange)=\"onBlockChange(i, $event)\"\n (addFieldRequest)=\"addField(block.code, $event)\"\n (notifySelectedField)=\"selectField($event)\"\n (deleteBlock)=\"deleteBlock($event)\">\n </lib-block-editor>\n }\r\n <ui-button type=\"bordered\" icon=\"ri-add-line\" color=\"black\" [text]=\"'form_builder.add_block' | uicTranslate\" (click)=\"addBlock()\"></ui-button>\r\n </div>\r\n </div>\r\n <!-- PROPERTIES -->\r\n <div class=\"formeditor-properties\" [style.width.px]=\"propertiesWidth()\">\n <div\n class=\"formeditor-properties-resize\"\n (mousedown)=\"startPropertiesResize($event)\">\n </div>\n <h3>Propiedades</h3>\n <div class=\"formeditor-properties-form\">\n @if (selectedField() ) {\r\n <lib-field-editor \n [config]=\"selectedField()!\"\n [focusRequiredField]=\"focusNewFieldCode() === selectedField()?.code\"\n [options]=\"dependencyOptions()\"\n (fieldChange)=\"onFieldChange($event)\">\n </lib-field-editor>\n }@else{\r\n <div class=\"no-selected-field\">\r\n <i class=\"ri-edit-box-line\"></i>\r\n <p>{{'form_builder.select_field_to_edit' | uicTranslate}}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }@else {\r\n <div class=\"form-preview\">\r\n <ui-form-wrapper \n [schema]=\"previewSchema()\"\n [selectOptionsResolver]=\"selectOptionsResolver\"\n [showButtons]=\"false\">\n </ui-form-wrapper>\n </div>\r\n }\r\n </div>\r\n\r\n", styles: [".formeditor{display:flex;flex-direction:column;overflow:hidden;background-color:var(--grey-100);border:solid 1px var(--grey-300);border-radius:5px;max-height:500px}.formeditor-header{background-color:#fff;padding:5px 10px;align-items:center;display:flex;gap:10px;border-bottom:solid 1px var(--grey-300)}.formeditor-body{display:flex;gap:10px;overflow:hidden;padding:10px;min-height:0}.formeditor-overflow{padding:5px;flex:1 1;overflow:auto;min-width:0}.formeditor-workarea{display:flex;flex-direction:column;gap:15px;height:fit-content}.formeditor-properties{width:250px;flex:0 0 auto;display:flex;flex-direction:column;position:relative;background-color:#fff;border:solid 1px var(--grey-300);border-radius:5px;min-height:0;min-width:250px;max-width:600px}.formeditor-properties-resize{position:absolute;top:0;bottom:0;left:-6px;width:10px;cursor:col-resize;z-index:1}.formeditor-properties>h3{padding:10px;border-bottom:solid 1px var(--grey-300)}.formeditor-properties-form{padding:10px;overflow:auto;flex:1 1 auto;min-height:0}.form-preview{padding:20px;overflow:auto;background-color:#fff}.focused{border:solid 1px var(--primary-400);border-radius:10px;box-shadow:0 0 0 3px var(--secondary-alpha)}.no-selected-field{width:100%;display:flex;flex-direction:column;justify-content:center;align-items:center;padding:20px 0;font-size:13px;gap:15px;text-align:center;color:var(--grey-400)}.no-selected-field i{font-size:24px}.cols-selector{margin-left:8px;padding-left:8px;border-left:solid 1px var(--grey-400);display:inline-flex;align-items:center;gap:5px}\n"] }]
10355
+ ], template: "<div class=\"formeditor\" [class.focused]=\"!isReadOnly()\">\r\n <div class=\"formeditor-header\">\r\n <div style=\"flex: 1 1;\">\r\n {{formTitle}} \r\n \r\n <div class=\"cols-selector\"> \r\n @if (isReadOnly()) {\r\n {{editableCols()}}\r\n }@else {\r\n <ui-select [options]=\"[{id: 1, text: '1'}, {id: 2, text: '2'}, {id: 3, text: '3'}, {id: 4, text: '4'}]\" [ngModel]=\"editableCols()\" (ngModelChange)=\"editableCols.set($event)\"></ui-select> \r\n }\r\n col(s)</div>\r\n </div>\r\n @if (isReadOnly()) {\r\n @if (!disabled) {\r\n <ui-button color=\"black\" icon=\"ri-edit-line\" text=\"Editar\" (click)=\"enableEditMode()\"></ui-button>\r\n }\r\n } @else {\r\n <ui-button color=\"black\" type=\"bordered\" icon=\"ri-eye-line\" [text]=\"'form_builder.preview_form' | uicTranslate\" (click)=\"printForm()\"></ui-button>\r\n <ui-button color=\"black\" type=\"bordered\" icon=\"ri-close-line\" text=\"Descartar cambios\" (click)=\"discardChanges()\"></ui-button>\r\n <ui-button color=\"black\" icon=\"ri-check-line\" [text]=\"'form_builder.submit_form' | uicTranslate\" (click)=\"submitForm()\"></ui-button>\r\n }\r\n </div>\r\n @if (!isReadOnly()) {\r\n <div class=\"formeditor-body\">\r\n \r\n <!-- BLOCKS -->\r\n <div class=\"formeditor-overflow\">\r\n <div class=\"formeditor-workarea\">\r\n @for (block of editableBlocks(); track block.code; let i = $index) {\r\n <lib-block-editor \r\n [block]=\"block\"\r\n [selectedFieldId]=\"selectedField()?.code ?? null\"\r\n [selectedSubFieldCode]=\"editingSubField()?.subField?.code ?? null\"\r\n [visibilityParentFieldCodes]=\"visibilityParentFieldCodes()\"\r\n (blockChange)=\"onBlockChange(i, $event)\"\r\n (addFieldRequest)=\"addField(block.code, $event)\"\r\n (notifySelectedField)=\"selectField($event)\"\r\n (notifySelectedSubField)=\"onSelectSubField($event)\"\r\n (deleteBlock)=\"deleteBlock($event)\">\r\n </lib-block-editor>\r\n }\r\n <ui-button type=\"bordered\" icon=\"ri-add-line\" color=\"black\" [text]=\"'form_builder.add_block' | uicTranslate\" (click)=\"addBlock()\"></ui-button>\r\n </div>\r\n </div>\r\n <!-- PROPERTIES -->\r\n <div class=\"formeditor-properties\" [style.width.px]=\"propertiesWidth()\">\r\n <div\r\n class=\"formeditor-properties-resize\"\r\n (mousedown)=\"startPropertiesResize($event)\">\r\n </div>\r\n <h3>Propiedades</h3>\r\n <div class=\"formeditor-properties-form\">\r\n @if (editingSubField()) {\r\n <div class=\"subfield-back-header\">\r\n <i class=\"ri-stack-line\"></i>\r\n <span>{{editingSubField()!.subField.fieldData.label || editingSubField()!.subField.fieldData.name}}</span>\r\n </div>\r\n <lib-field-editor\r\n [config]=\"editingSubField()!.subField\"\r\n [isSubField]=\"true\"\r\n [options]=\"{}\"\r\n (fieldChange)=\"onSubFieldChange($event)\">\r\n </lib-field-editor>\r\n } @else if (selectedField()) {\r\n <lib-field-editor \r\n [config]=\"selectedField()!\"\r\n [focusRequiredField]=\"focusNewFieldCode() === selectedField()?.code\"\r\n [options]=\"dependencyOptions()\"\r\n (fieldChange)=\"onFieldChange($event)\">\r\n </lib-field-editor>\r\n }@else{\r\n <div class=\"no-selected-field\">\r\n <i class=\"ri-edit-box-line\"></i>\r\n <p>{{'form_builder.select_field_to_edit' | uicTranslate}}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }@else {\r\n <div class=\"form-preview\">\r\n <ui-form-wrapper \r\n [schema]=\"previewSchema()\"\r\n [selectOptionsResolver]=\"selectOptionsResolver\"\r\n [showButtons]=\"false\">\r\n </ui-form-wrapper>\r\n </div>\r\n }\r\n </div>\r\n\r\n", styles: [".formeditor{display:flex;flex-direction:column;overflow:hidden;background-color:var(--grey-100);border:solid 1px var(--grey-300);border-radius:5px;max-height:500px}.formeditor-header{background-color:#fff;padding:5px 10px;align-items:center;display:flex;gap:10px;border-bottom:solid 1px var(--grey-300)}.formeditor-body{display:flex;gap:10px;overflow:hidden;padding:10px;min-height:0}.formeditor-overflow{padding:5px;flex:1 1;overflow:auto;min-width:0}.formeditor-workarea{display:flex;flex-direction:column;gap:15px;height:fit-content}.formeditor-properties{width:250px;flex:0 0 auto;display:flex;flex-direction:column;position:relative;background-color:#fff;border:solid 1px var(--grey-300);border-radius:5px;min-height:0;min-width:250px;max-width:600px}.formeditor-properties-resize{position:absolute;top:0;bottom:0;left:-6px;width:10px;cursor:col-resize;z-index:1}.formeditor-properties>h3{padding:10px;border-bottom:solid 1px var(--grey-300)}.formeditor-properties-form{padding:10px;overflow:auto;flex:1 1 auto;min-height:0}.form-preview{padding:20px;overflow:auto;background-color:#fff}.focused{border:solid 1px var(--primary-400);border-radius:10px;box-shadow:0 0 0 3px var(--secondary-alpha)}.no-selected-field{width:100%;display:flex;flex-direction:column;justify-content:center;align-items:center;padding:20px 0;font-size:13px;gap:15px;text-align:center;color:var(--grey-400)}.no-selected-field i{font-size:24px}.cols-selector{margin-left:8px;padding-left:8px;border-left:solid 1px var(--grey-400);display:inline-flex;align-items:center;gap:5px}.subfield-back-header{display:flex;align-items:center;gap:6px;padding:0 0 10px;border-bottom:solid 1px var(--grey-200);margin-bottom:10px;color:var(--grey-500);font-size:12px}.subfield-back-header i{font-size:14px;color:var(--primary-500)}.subfield-back-header span{font-weight:600;color:var(--grey-700);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"] }]
9657
10356
  }], ctorParameters: () => [], propDecorators: { disabled: [{
9658
10357
  type: Input
9659
10358
  }], formTitle: [{
@@ -10471,5 +11170,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
10471
11170
  * Generated bundle index. Do not edit.
10472
11171
  */
10473
11172
 
10474
- export { BASE_FORM_FIELDS, BRANCH_FORM_FIELDS, DROPDOWN_OVERLAY_CONTROLS, EXTRA_FORM_FIELDS, FIELDS_CONFIG, FirstCapitalPipe, LAYOUT_FORM_FIELDS, MODAL_CLOSE_EVENT, MODAL_CLOSE_REQUEST, MODAL_COMPONENT, MODAL_CONFIG, MODAL_DATA, UIC_TRANSLATE_CONFIG, UiDropdownCloseDirective, UiModalRef, UicAccordionComponent, UicActionButtonComponent, UicAlertContainerComponent, UicButtonComponent, UicCheckboxComponent, UicDatePickerComponent, UicDropdownContainerComponent, UicEditableTableComponent, UicExcelTableComponent, UicFileInputComponent, UicFormWrapperComponent, UicInputComponent, UicKpiCardComponent, UicModalService, UicMultySearcherComponent, UicMultySelectComponent, UicNameInitsPipe, UicOverlayCardComponent, UicPhoneInputComponent, UicPoolOptionsComponent, UicPortletCardComponent, UicProgressBarComponent, UicPushAlertService, UicRadioComponent, UicRuleBuilderComponent, UicSearcherComponent, UicSelectComponent, UicShortTableComponent, UicSignaturePadComponent, UicSkeletonCardsComponent, UicSkeletonLoaderComponent, UicSliderComponent, UicStepTabsComponent, UicStepsFormComponent, UicSwichComponent, UicTableComponent, UicTabsButtonComponent, UicTagSelectorComponent, UicTextareaAutoresizeDirective, UicTextareaAutoresizeMaxRowsDirective, UicTextareaAutoresizeMinRowsDirective, UicTimePickerComponent, UicTinyAlertService, UicToolTipDirective, UicTranslatePipe, UicTranslateService, UicTreeAdminComponent, UicUserFormbuilderComponent, UicWorkPanelComponent, animatedRow, fadeAndRise, fadeBackdrop, helperFormMapFormdataToObject, helperShowFormFromBuilder, helperTableMapDatoToColums, highlightRow, isMobile, provideUicTranslateConfig, pushTop, sideModal, simpleFade };
11173
+ export { BASE_FORM_FIELDS, BRANCH_FORM_FIELDS, DROPDOWN_OVERLAY_CONTROLS, EXTRA_FORM_FIELDS, FIELDS_CONFIG, FirstCapitalPipe, LAYOUT_FORM_FIELDS, MODAL_CLOSE_EVENT, MODAL_CLOSE_REQUEST, MODAL_COMPONENT, MODAL_CONFIG, MODAL_DATA, UIC_TRANSLATE_CONFIG, UiDropdownCloseDirective, UiModalRef, UicAccordionComponent, UicActionButtonComponent, UicAlertContainerComponent, UicButtonComponent, UicCheckboxComponent, UicDatePickerComponent, UicDropdownContainerComponent, UicEditableTableComponent, UicExcelTableComponent, UicFileInputComponent, UicFormWrapperComponent, UicInputComponent, UicKpiCardComponent, UicModalService, UicMultySearcherComponent, UicMultySelectComponent, UicNameInitsPipe, UicOverlayCardComponent, UicPhoneInputComponent, UicPoolOptionsComponent, UicPortletCardComponent, UicProgressBarComponent, UicPushAlertService, UicRadioComponent, UicRepeaterComponent, UicRuleBuilderComponent, UicSearcherComponent, UicSelectComponent, UicShortTableComponent, UicSignaturePadComponent, UicSkeletonCardsComponent, UicSkeletonLoaderComponent, UicSliderComponent, UicStepTabsComponent, UicStepsFormComponent, UicSwichComponent, UicTableComponent, UicTabsButtonComponent, UicTagSelectorComponent, UicTextareaAutoresizeDirective, UicTextareaAutoresizeMaxRowsDirective, UicTextareaAutoresizeMinRowsDirective, UicTimePickerComponent, UicTinyAlertService, UicToolTipDirective, UicTranslatePipe, UicTranslateService, UicTreeAdminComponent, UicUserFormbuilderComponent, UicWorkPanelComponent, VISIBILITY_OPERATOR_OPTIONS, animatedRow, fadeAndRise, fadeBackdrop, helperFormMapFormdataToObject, helperShowFormFromBuilder, helperTableMapDatoToColums, highlightRow, isMobile, provideUicTranslateConfig, pushTop, sideModal, simpleFade };
10475
11174
  //# sourceMappingURL=ui-core-abv.mjs.map