@decaf-ts/for-angular 0.0.23 → 0.0.25

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.
Files changed (36) hide show
  1. package/components/component-renderer/component-renderer.component.d.ts +3 -2
  2. package/components/crud-field/crud-field.component.d.ts +4 -2
  3. package/components/fieldset/fieldset.component.d.ts +10 -1
  4. package/components/for-angular-components.module.d.ts +3 -2
  5. package/components/index.d.ts +1 -0
  6. package/components/layout/layout.component.d.ts +1 -24
  7. package/components/model-renderer/model-renderer.component.d.ts +6 -1
  8. package/components/stepped-form/stepped-form.component.d.ts +255 -0
  9. package/engine/NgxBaseComponent.d.ts +2 -2
  10. package/engine/NgxCrudFormField.d.ts +1 -0
  11. package/engine/NgxFormService.d.ts +381 -48
  12. package/engine/NgxRenderingEngine.d.ts +4 -2
  13. package/engine/interfaces.d.ts +1 -1
  14. package/engine/types.d.ts +4 -3
  15. package/esm2022/components/component-renderer/component-renderer.component.mjs +10 -4
  16. package/esm2022/components/crud-field/crud-field.component.mjs +14 -3
  17. package/esm2022/components/crud-form/crud-form.component.mjs +3 -3
  18. package/esm2022/components/empty-state/empty-state.component.mjs +2 -2
  19. package/esm2022/components/fieldset/fieldset.component.mjs +5 -3
  20. package/esm2022/components/for-angular-components.module.mjs +12 -7
  21. package/esm2022/components/index.mjs +2 -1
  22. package/esm2022/components/layout/layout.component.mjs +4 -29
  23. package/esm2022/components/list/list.component.mjs +3 -3
  24. package/esm2022/components/model-renderer/model-renderer.component.mjs +10 -3
  25. package/esm2022/components/stepped-form/stepped-form.component.mjs +306 -0
  26. package/esm2022/engine/NgxBaseComponent.mjs +10 -4
  27. package/esm2022/engine/NgxCrudFormField.mjs +19 -17
  28. package/esm2022/engine/NgxFormService.mjs +438 -57
  29. package/esm2022/engine/NgxRenderingEngine.mjs +21 -10
  30. package/esm2022/engine/ValidatorFactory.mjs +4 -4
  31. package/esm2022/engine/interfaces.mjs +1 -1
  32. package/esm2022/engine/types.mjs +1 -1
  33. package/esm2022/i18n/data/en.json +5 -0
  34. package/fesm2022/decaf-ts-for-angular.mjs +837 -137
  35. package/fesm2022/decaf-ts-for-angular.mjs.map +1 -1
  36. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { UIKeys, parseValueByType, HTML5InputTypes, HTML5CheckTypes, escapeHtml, parseToNumber, RenderingEngine, RenderingError } from '@decaf-ts/ui-decorators';
1
+ import { UIKeys, parseValueByType, HTML5CheckTypes, escapeHtml, HTML5InputTypes, parseToNumber, RenderingEngine, RenderingError } from '@decaf-ts/ui-decorators';
2
2
  import * as i0 from '@angular/core';
3
3
  import { InjectionToken, NgModule, isDevMode, reflectComponentType, inject, EnvironmentInjector, EventEmitter, ViewContainerRef, TemplateRef, ViewChild, Input, Output, Component, ElementRef, HostListener, CUSTOM_ELEMENTS_SCHEMA, Inject, ChangeDetectorRef, Renderer2, Injector, Directive } from '@angular/core';
4
4
  import { CommonModule, NgComponentOutlet, Location } from '@angular/common';
@@ -14,9 +14,9 @@ import { apply, metadata } from '@decaf-ts/reflection';
14
14
  import { IonInput, IonItem, IonCheckbox, IonRadioGroup, IonRadio, IonSelect, IonSelectOption, IonLabel, IonText, IonTextarea, IonIcon, IonButton, IonCard, IonCardContent, IonAccordionGroup, IonAccordion, IonList, IonReorder, IonReorderGroup, IonSearchbar, IonChip, IonListHeader, IonItemSliding, IonItemOptions, IonItemOption, IonContent, IonPopover, IonRefresher, IonThumbnail, IonSkeletonText, IonRefresherContent, IonInfiniteScroll, IonInfiniteScrollContent, IonLoading } from '@ionic/angular/standalone';
15
15
  import { addIcons } from 'ionicons';
16
16
  import * as allIcons from 'ionicons/icons';
17
- import { chevronUpOutline, chevronDownOutline, createOutline, alertCircleOutline, chevronForwardOutline, chevronBackOutline } from 'ionicons/icons';
17
+ import { chevronUpOutline, chevronDownOutline, createOutline, alertCircleOutline, chevronForwardOutline, chevronBackOutline, arrowBackOutline, arrowForwardOutline } from 'ionicons/icons';
18
18
  import { TranslateHttpLoader } from '@ngx-translate/http-loader';
19
- import { forkJoin, fromEvent, debounceTime, Subject } from 'rxjs';
19
+ import { forkJoin, fromEvent, debounceTime, Subject, timer } from 'rxjs';
20
20
  import { map } from 'rxjs/operators';
21
21
  import { Repository, OrderDirection, Condition } from '@decaf-ts/core';
22
22
  import { DomSanitizer } from '@angular/platform-browser';
@@ -226,12 +226,12 @@ class ValidatorFactory {
226
226
  if (!Validation.keys().includes(key))
227
227
  throw new Error('Unsupported custom validation');
228
228
  const validatorFn = (control) => {
229
- const { name, type } = fieldProps;
229
+ const { type } = fieldProps;
230
230
  const { validatorKey, props } = resolveValidatorKeyProps(key, fieldProps[key], type);
231
231
  const validator = Validation.get(validatorKey);
232
232
  // parseValueByType does not support undefined values
233
233
  const value = typeof control.value !== 'undefined'
234
- ? parseValueByType(type, type === HTML5InputTypes.CHECKBOX ? name : control.value, fieldProps)
234
+ ? parseValueByType(type, control.value, fieldProps)
235
235
  : undefined;
236
236
  // Create a proxy to enable access to parent and child values
237
237
  let proxy = ValidatorFactory.createProxy({});
@@ -875,12 +875,61 @@ class NgxFormService {
875
875
  * @memberOf NgxFormService
876
876
  */
877
877
  static { this.formRegistry = new Map(); }
878
+ static { this.pageMapper = {}; }
879
+ /**
880
+ * @description Creates a new form group or form array with the specified identifier.
881
+ * @summary Generates a FormGroup or FormArray based on the provided properties. If pages are specified
882
+ * and greater than 1, creates a FormArray; otherwise creates a FormGroup. The form can optionally
883
+ * be registered in the global form registry for later access throughout the application.
884
+ *
885
+ * @param {string} id - Unique identifier for the form
886
+ * @param {Partial<IComponentInput>} [props={}] - Configuration properties for the form
887
+ * @param {boolean} [registry=true] - Whether to register the form in the global registry
888
+ * @return {FormGroup | FormArray} The created form instance
889
+ *
890
+ * @mermaid
891
+ * sequenceDiagram
892
+ * participant C as Component
893
+ * participant NFS as NgxFormService
894
+ * participant FR as Form Registry
895
+ * participant AF as Angular Forms
896
+ *
897
+ * C->>NFS: createForm(id, props, registry)
898
+ * NFS->>FR: Check if form exists
899
+ * alt Form doesn't exist
900
+ * alt props.pages > 1
901
+ * NFS->>AF: new FormArray([])
902
+ * else
903
+ * NFS->>AF: new FormGroup({})
904
+ * end
905
+ * alt registry is true
906
+ * NFS->>FR: addRegistry(id, form)
907
+ * end
908
+ * end
909
+ * NFS-->>C: Return FormGroup | FormArray
910
+ *
911
+ * @static
912
+ * @memberOf NgxFormService
913
+ */
914
+ static createForm(id, props = {}, registry = true) {
915
+ const form = this.formRegistry.get(id) ?? (props?.pages && props?.pages > 1 ? new FormArray([]) : new FormGroup({}));
916
+ if (!this.formRegistry.has(id) && registry)
917
+ this.addRegistry(id, form);
918
+ return form;
919
+ }
878
920
  /**
879
921
  * @description Adds a form to the registry.
880
- * @summary Registers a FormGroup with a unique identifier. Throws an error if the identifier is already in use.
881
- * @param {string} formId - The unique identifier for the form.
882
- * @param {FormGroup} formGroup - The FormGroup to be registered.
883
- * @throws {Error} If a FormGroup with the given id is already registered.
922
+ * @summary Registers a FormGroup or FormArray with a unique identifier for global access throughout
923
+ * the application. This allows forms to be retrieved and managed centrally. Throws an error if
924
+ * the identifier is already in use to prevent conflicts.
925
+ *
926
+ * @param {string} formId - The unique identifier for the form
927
+ * @param {FormParent} formGroup - The FormGroup or FormArray to be registered
928
+ * @return {void}
929
+ * @throws {Error} If a FormGroup with the given id is already registered
930
+ *
931
+ * @static
932
+ * @memberOf NgxFormService
884
933
  */
885
934
  static addRegistry(formId, formGroup) {
886
935
  if (this.formRegistry.has(formId))
@@ -889,8 +938,15 @@ class NgxFormService {
889
938
  }
890
939
  /**
891
940
  * @description Removes a form from the registry.
892
- * @summary Deletes a FormGroup from the registry using its unique identifier.
893
- * @param {string} formId - The unique identifier of the form to be removed.
941
+ * @summary Deletes a FormGroup or FormArray from the registry using its unique identifier.
942
+ * This cleans up the registry and allows the identifier to be reused. The form itself
943
+ * is not destroyed, only removed from the central registry.
944
+ *
945
+ * @param {string} formId - The unique identifier of the form to be removed
946
+ * @return {void}
947
+ *
948
+ * @static
949
+ * @memberOf NgxFormService
894
950
  */
895
951
  static removeRegistry(formId) {
896
952
  this.formRegistry.delete(formId);
@@ -898,9 +954,38 @@ class NgxFormService {
898
954
  /**
899
955
  * @description Resolves the parent group and control name from a path.
900
956
  * @summary Traverses the form group structure to find the parent group and control name for a given path.
901
- * @param {FormGroup} formGroup - The root FormGroup.
902
- * @param {string} path - The path to the control.
903
- * @return {FormParentGroup} A tuple containing the parent FormGroup and the control name.
957
+ * Handles complex nested structures including arrays and sub-groups. Creates missing intermediate
958
+ * groups as needed and properly configures FormArray controls for multiple value scenarios.
959
+ *
960
+ * @param {FormGroup} formGroup - The root FormGroup to traverse
961
+ * @param {string} path - The dot-separated path to the control (e.g., 'user.address.street')
962
+ * @param {IComponentInput} componentProps - Properties defining the component configuration
963
+ * @param {KeyValue} parentProps - Properties from the parent component for context
964
+ * @return {FormParentGroup} A tuple containing the parent FormGroup and the control name
965
+ *
966
+ * @private
967
+ * @mermaid
968
+ * sequenceDiagram
969
+ * participant NFS as NgxFormService
970
+ * participant FG as FormGroup
971
+ * participant FA as FormArray
972
+ *
973
+ * NFS->>NFS: Split path into parts
974
+ * loop For each path part
975
+ * alt Control doesn't exist
976
+ * alt isMultiple and part is childOf
977
+ * NFS->>FA: new FormArray([new FormGroup({})])
978
+ * else
979
+ * NFS->>FG: new FormGroup({})
980
+ * end
981
+ * NFS->>FG: addControl(part, newControl)
982
+ * end
983
+ * NFS->>NFS: Navigate to next level
984
+ * end
985
+ * NFS-->>NFS: Return [parentGroup, controlName]
986
+ *
987
+ * @static
988
+ * @memberOf NgxFormService
904
989
  */
905
990
  static resolveParentGroup(formGroup, path, componentProps, parentProps) {
906
991
  const isMultiple = parentProps?.['multiple'] || parentProps?.['type'] === 'Array' || false;
@@ -1079,19 +1164,62 @@ class NgxFormService {
1079
1164
  if (!parentGroup.get(controlName)) {
1080
1165
  const control = NgxFormService.fromProps(componentProps, componentProps.updateMode || 'change');
1081
1166
  NgxFormService.register(control, componentProps);
1082
- parentGroup.addControl(controlName, control);
1167
+ if (parentGroup instanceof FormGroup) {
1168
+ parentGroup.addControl(controlName, control);
1169
+ }
1170
+ if (parentGroup instanceof FormArray) {
1171
+ const root = parentGroup.controls[componentProps?.['page'] - 1];
1172
+ if (root) {
1173
+ root.addControl(controlName, control);
1174
+ }
1175
+ else {
1176
+ parentGroup.push({ [controlName]: control });
1177
+ }
1178
+ }
1083
1179
  }
1084
- componentProps['formGroup'] = parentGroup;
1180
+ const root = parentGroup instanceof FormArray ? parentGroup.controls[componentProps?.['page'] - 1] : parentGroup;
1181
+ componentProps['formGroup'] = root;
1085
1182
  componentProps['formControl'] = parentGroup.get(controlName);
1086
1183
  componentProps['multiple'] = isMultiple;
1087
1184
  }
1088
1185
  /**
1089
1186
  * @description Retrieves a control from a registered form.
1090
1187
  * @summary Finds and returns an AbstractControl from a registered form using the form id and optional path.
1091
- * @param {string} formId - The unique identifier of the form.
1092
- * @param {string} [path] - The path to the control within the form.
1093
- * @return {AbstractControl} The requested AbstractControl.
1094
- * @throws {Error} If the form is not found in the registry or the control is not found in the form.
1188
+ * This method provides centralized access to form controls across the application by leveraging
1189
+ * the form registry system.
1190
+ *
1191
+ * @param {string} formId - The unique identifier of the form in the registry
1192
+ * @param {string} [path] - The optional dot-separated path to a specific control within the form
1193
+ * @return {AbstractControl} The requested AbstractControl (FormGroup, FormArray, or FormControl)
1194
+ * @throws {Error} If the form is not found in the registry or the control is not found in the form
1195
+ *
1196
+ * @mermaid
1197
+ * sequenceDiagram
1198
+ * participant C as Component
1199
+ * participant NFS as NgxFormService
1200
+ * participant FR as Form Registry
1201
+ *
1202
+ * C->>NFS: getControlFromForm(formId, path?)
1203
+ * NFS->>FR: Get form by formId
1204
+ * alt Form not found
1205
+ * FR-->>NFS: null
1206
+ * NFS-->>C: Throw Error
1207
+ * else Form found
1208
+ * FR-->>NFS: Return form
1209
+ * alt path provided
1210
+ * NFS->>NFS: form.get(path)
1211
+ * alt Control not found
1212
+ * NFS-->>C: Throw Error
1213
+ * else
1214
+ * NFS-->>C: Return control
1215
+ * end
1216
+ * else
1217
+ * NFS-->>C: Return form
1218
+ * end
1219
+ * end
1220
+ *
1221
+ * @static
1222
+ * @memberOf NgxFormService
1095
1223
  */
1096
1224
  static getControlFromForm(formId, path) {
1097
1225
  const form = this.formRegistry.get(formId);
@@ -1104,13 +1232,77 @@ class NgxFormService {
1104
1232
  throw new Error(`Control with path '${path}' not found in form '${formId}'.`);
1105
1233
  return control;
1106
1234
  }
1235
+ /**
1236
+ * @description Creates a form from UI model metadata children.
1237
+ * @summary Generates a FormGroup from an array of UIModelMetadata objects, extracting component
1238
+ * properties and creating appropriate form controls. This method is specifically designed to work
1239
+ * with the UI decorator system and provides automatic form generation from metadata.
1240
+ *
1241
+ * @param {string} id - Unique identifier for the form
1242
+ * @param {boolean} [registry=false] - Whether to register the created form in the global registry
1243
+ * @param {UIModelMetadata[]} [children] - Array of UI model metadata objects to create controls from
1244
+ * @return {FormGroup} The created FormGroup with controls for each child metadata
1245
+ *
1246
+ * @mermaid
1247
+ * sequenceDiagram
1248
+ * participant C as Component
1249
+ * participant NFS as NgxFormService
1250
+ * participant AF as Angular Forms
1251
+ *
1252
+ * C->>NFS: createFormFromChildren(id, registry, children)
1253
+ * NFS->>AF: new FormGroup({})
1254
+ * loop For each child metadata
1255
+ * NFS->>NFS: addFormControl(form, child.props)
1256
+ * NFS->>AF: Create and add FormControl
1257
+ * end
1258
+ * alt registry is true
1259
+ * NFS->>NFS: addRegistry(id, form)
1260
+ * end
1261
+ * NFS-->>C: Return FormGroup
1262
+ *
1263
+ * @static
1264
+ * @memberOf NgxFormService
1265
+ */
1266
+ static createFormFromChildren(id, registry = false, children) {
1267
+ const form = new FormGroup({});
1268
+ if (children?.length)
1269
+ children.forEach(child => {
1270
+ this.addFormControl(form, child.props);
1271
+ });
1272
+ if (registry)
1273
+ this.addRegistry(id, form);
1274
+ return form;
1275
+ }
1107
1276
  /**
1108
1277
  * @description Creates a form from component configurations.
1109
1278
  * @summary Generates a FormGroup based on an array of component configurations and optionally registers it.
1110
- * @param {string} id - The unique identifier for the form.
1111
- * @param {IComponentConfig[]} components - An array of component configurations.
1112
- * @param {boolean} [registry=false] - Whether to register the created form.
1113
- * @return {FormGroup} The created FormGroup.
1279
+ * This method processes component input configurations to create appropriate form controls with
1280
+ * validation and initial values.
1281
+ *
1282
+ * @param {string} id - The unique identifier for the form
1283
+ * @param {IComponentConfig[]} components - An array of component configurations defining the form structure
1284
+ * @param {boolean} [registry=false] - Whether to register the created form in the global registry
1285
+ * @return {FormGroup} The created FormGroup with controls for each component configuration
1286
+ *
1287
+ * @mermaid
1288
+ * sequenceDiagram
1289
+ * participant C as Component
1290
+ * participant NFS as NgxFormService
1291
+ * participant AF as Angular Forms
1292
+ *
1293
+ * C->>NFS: createFormFromComponents(id, components, registry)
1294
+ * NFS->>AF: new FormGroup({})
1295
+ * loop For each component config
1296
+ * NFS->>NFS: addFormControl(form, component.inputs)
1297
+ * NFS->>AF: Create and add FormControl
1298
+ * end
1299
+ * alt registry is true
1300
+ * NFS->>NFS: addRegistry(id, form)
1301
+ * end
1302
+ * NFS-->>C: Return FormGroup
1303
+ *
1304
+ * @static
1305
+ * @memberOf NgxFormService
1114
1306
  */
1115
1307
  static createFormFromComponents(id, components, registry = false) {
1116
1308
  const form = new FormGroup({});
@@ -1124,14 +1316,61 @@ class NgxFormService {
1124
1316
  /**
1125
1317
  * @description Adds a control to a form based on component properties.
1126
1318
  * @summary Creates and adds a form control to a form (existing or new) based on the provided component properties.
1127
- * @param {string} id - The unique identifier of the form.
1128
- * @param {FieldProperties} componentProperties - The properties of the component to create the control from.
1129
- * @return {AbstractControl} The form or created control.
1319
+ * Handles multi-page forms by managing FormArray structures and proper indexing. This method supports
1320
+ * complex form scenarios including nested controls and page-based form organization.
1321
+ *
1322
+ * @param {string} id - The unique identifier of the form
1323
+ * @param {FieldProperties} componentProperties - The properties of the component to create the control from
1324
+ * @param {FieldProperties} [parentProps] - Optional parent properties for context and configuration
1325
+ * @return {AbstractControl} The form or created control
1326
+ *
1327
+ * @mermaid
1328
+ * sequenceDiagram
1329
+ * participant C as Component
1330
+ * participant NFS as NgxFormService
1331
+ * participant F as Form
1332
+ *
1333
+ * C->>NFS: addControlFromProps(id, componentProps, parentProps?)
1334
+ * NFS->>NFS: createForm(id, parentProps, true)
1335
+ * alt Multi-page form (parentProps.pages > 1)
1336
+ * NFS->>NFS: Calculate page index
1337
+ * NFS->>F: Get or create FormGroup at index
1338
+ * NFS->>NFS: Set form to page FormGroup
1339
+ * end
1340
+ * alt componentProperties has path
1341
+ * NFS->>NFS: addFormControl(form, componentProperties, parentProps)
1342
+ * end
1343
+ * NFS-->>C: Return form/control
1344
+ *
1345
+ * @static
1346
+ * @memberOf NgxFormService
1130
1347
  */
1131
1348
  static addControlFromProps(id, componentProperties, parentProps) {
1132
- const form = this.formRegistry.get(id) ?? new FormGroup({});
1133
- if (!this.formRegistry.has(id))
1134
- this.addRegistry(id, form);
1349
+ let form = this.createForm(id, parentProps, true);
1350
+ const formLength = form.length;
1351
+ if (parentProps?.pages && parentProps?.pages > 1) {
1352
+ let index = componentProperties.page || parentProps.page;
1353
+ if (!(typeof index === 'number') || index === 0)
1354
+ throw Error(`Property 'page' is required and greather than 0 on ${componentProperties.name}`);
1355
+ if (index > formLength) {
1356
+ if (form?.['lastIndex'] && index === form['lastIndex']['page']) {
1357
+ index = form['lastIndex']['index'];
1358
+ }
1359
+ else {
1360
+ form['lastIndex'] = {
1361
+ page: index,
1362
+ index: formLength + 1
1363
+ };
1364
+ index = formLength + 1;
1365
+ }
1366
+ }
1367
+ let group = form.controls[index - 1];
1368
+ if (!group) {
1369
+ group = new FormGroup({});
1370
+ form.insert(index, group);
1371
+ }
1372
+ form = group;
1373
+ }
1135
1374
  if (componentProperties.path)
1136
1375
  this.addFormControl(form, componentProperties, parentProps);
1137
1376
  return form;
@@ -1139,8 +1378,45 @@ class NgxFormService {
1139
1378
  /**
1140
1379
  * @description Retrieves form data from a FormGroup.
1141
1380
  * @summary Extracts and processes the data from a FormGroup, handling different input types and nested form groups.
1142
- * @param {FormGroup} formGroup - The FormGroup to extract data from.
1143
- * @return {Record<string, unknown>} An object containing the form data.
1381
+ * Performs type conversion for various HTML5 input types, validates nested controls, and manages
1382
+ * multiple control scenarios. Automatically enables all group controls after data extraction.
1383
+ *
1384
+ * @param {FormGroup} formGroup - The FormGroup to extract data from
1385
+ * @return {Record<string, unknown>} An object containing the processed form data with proper type conversions
1386
+ *
1387
+ * @mermaid
1388
+ * sequenceDiagram
1389
+ * participant C as Component
1390
+ * participant NFS as NgxFormService
1391
+ * participant FG as FormGroup
1392
+ * participant FC as FormControl
1393
+ *
1394
+ * C->>NFS: getFormData(formGroup)
1395
+ * loop For each control in formGroup
1396
+ * alt Control is not FormControl
1397
+ * NFS->>NFS: Recursive getFormData(control)
1398
+ * alt parentProps.multiple and !isValid
1399
+ * NFS->>NFS: reset(control)
1400
+ * end
1401
+ * else Control is FormControl
1402
+ * NFS->>FC: Get control value
1403
+ * NFS->>NFS: Apply type conversion based on props.type
1404
+ * alt HTML5CheckTypes
1405
+ * NFS->>NFS: Keep boolean value
1406
+ * else NUMBER type
1407
+ * NFS->>NFS: parseToNumber(value)
1408
+ * else DATE/DATETIME types
1409
+ * NFS->>NFS: new Date(value)
1410
+ * else Other types
1411
+ * NFS->>NFS: escapeHtml(value)
1412
+ * end
1413
+ * end
1414
+ * end
1415
+ * NFS->>NFS: enableAllGroupControls(formGroup)
1416
+ * NFS-->>C: Return processed data object
1417
+ *
1418
+ * @static
1419
+ * @memberOf NgxFormService
1144
1420
  */
1145
1421
  static getFormData(formGroup) {
1146
1422
  const data = {};
@@ -1185,10 +1461,51 @@ class NgxFormService {
1185
1461
  /**
1186
1462
  * @description Validates fields in a form control or form group.
1187
1463
  * @summary Recursively validates all fields in a form control or form group, marking them as touched and dirty.
1188
- * @param {AbstractControl} control - The control or form group to validate.
1189
- * @param {string} [path] - The path to the control within the form.
1190
- * @return {boolean} True if all fields are valid, false otherwise.
1191
- * @throws {Error} If no control is found at the specified path or if the control type is unknown.
1464
+ * Performs comprehensive validation including uniqueness checks for primary keys in FormArray scenarios.
1465
+ * This method ensures all validation rules are applied and form state is properly updated.
1466
+ *
1467
+ * @param {AbstractControl} control - The control or form group to validate
1468
+ * @param {string} [pk] - Optional primary key field name for uniqueness validation
1469
+ * @param {string} [path] - The path to the control within the form for error reporting
1470
+ * @return {boolean} True if all fields are valid, false otherwise
1471
+ * @throws {Error} If no control is found at the specified path or if the control type is unknown
1472
+ *
1473
+ * @mermaid
1474
+ * sequenceDiagram
1475
+ * participant C as Component
1476
+ * participant NFS as NgxFormService
1477
+ * participant FC as FormControl
1478
+ * participant FG as FormGroup
1479
+ * participant FA as FormArray
1480
+ *
1481
+ * C->>NFS: validateFields(control, pk?, path?)
1482
+ * alt Control is FormControl
1483
+ * NFS->>FC: markAsTouched()
1484
+ * NFS->>FC: markAsDirty()
1485
+ * NFS->>FC: updateValueAndValidity()
1486
+ * alt Is in FormArray group
1487
+ * NFS->>NFS: Check uniqueness in group
1488
+ * alt Not unique
1489
+ * NFS->>FC: setErrors({notUnique: true})
1490
+ * end
1491
+ * end
1492
+ * NFS-->>C: Return control.valid
1493
+ * else Control is FormGroup
1494
+ * loop For each child control
1495
+ * NFS->>NFS: Recursive validateFields(child)
1496
+ * end
1497
+ * NFS-->>C: Return allValid
1498
+ * else Control is FormArray
1499
+ * loop For each array control
1500
+ * NFS->>NFS: Recursive validateFields(child)
1501
+ * end
1502
+ * NFS-->>C: Return allValid
1503
+ * else Unknown control type
1504
+ * NFS-->>C: Throw Error
1505
+ * end
1506
+ *
1507
+ * @static
1508
+ * @memberOf NgxFormService
1192
1509
  */
1193
1510
  static validateFields(control, pk, path) {
1194
1511
  control = path ? control.get(path) : control;
@@ -1228,13 +1545,13 @@ class NgxFormService {
1228
1545
  });
1229
1546
  }
1230
1547
  }
1231
- function getControlName(control) {
1232
- const group = control.parent;
1233
- if (!group)
1234
- return null;
1235
- return Object.keys(group.controls).find(name => control === group.get(name)) || null;
1236
- }
1237
- return !getControlName(control) ? true : control.valid;
1548
+ // function getControlName(control: AbstractControl): string | null {
1549
+ // const group = control.parent as FormGroup;
1550
+ // if (!group)
1551
+ // return null;
1552
+ // return Object.keys(group.controls).find(name => control === group.get(name)) || null;
1553
+ // }
1554
+ return control.valid;
1238
1555
  }
1239
1556
  /**
1240
1557
  * @description Generates validators from component properties.
@@ -1252,10 +1569,38 @@ class NgxFormService {
1252
1569
  }
1253
1570
  /**
1254
1571
  * @description Creates a FormControl from component properties.
1255
- * @summary Generates a FormControl with validators based on the provided component properties.
1256
- * @param {FieldProperties} props - The component properties.
1257
- * @param {FieldUpdateMode} [updateMode='change'] - The update mode for the control.
1258
- * @return {FormControl} The created FormControl.
1572
+ * @summary Generates a FormControl with validators and initial configuration based on the provided
1573
+ * component properties. Handles different input types, sets initial values, and configures
1574
+ * validation rules and update modes.
1575
+ *
1576
+ * @param {FieldProperties} props - The component properties defining the control configuration
1577
+ * @param {FieldUpdateMode} [updateMode='change'] - The update mode for the control ('change', 'blur', 'submit')
1578
+ * @return {FormControl} The created FormControl with proper configuration and validators
1579
+ *
1580
+ * @mermaid
1581
+ * sequenceDiagram
1582
+ * participant C as Component
1583
+ * participant NFS as NgxFormService
1584
+ * participant VF as ValidatorFactory
1585
+ * participant AF as Angular Forms
1586
+ *
1587
+ * C->>NFS: fromProps(props, updateMode?)
1588
+ * NFS->>NFS: validatorsFromProps(props)
1589
+ * NFS->>VF: Create validators from props
1590
+ * VF-->>NFS: Return validator array
1591
+ * NFS->>NFS: Compose validators
1592
+ * alt props.value exists and not checkbox
1593
+ * alt props.type is DATE
1594
+ * NFS->>NFS: Validate date format
1595
+ * end
1596
+ * NFS->>NFS: Set initial value
1597
+ * end
1598
+ * NFS->>AF: new FormControl(config)
1599
+ * AF-->>NFS: Return FormControl
1600
+ * NFS-->>C: Return configured FormControl
1601
+ *
1602
+ * @static
1603
+ * @memberOf NgxFormService
1259
1604
  */
1260
1605
  static fromProps(props, updateMode = 'change') {
1261
1606
  const validators = this.validatorsFromProps(props);
@@ -1273,21 +1618,50 @@ class NgxFormService {
1273
1618
  });
1274
1619
  }
1275
1620
  /**
1276
- * @description Retrieves properties from a FormControl.
1277
- * @summary Gets the FieldProperties associated with a FormControl from the internal WeakMap.
1278
- * @param {FormControl} control - The FormControl to get properties for.
1279
- * @return {FieldProperties} The properties associated with the control.
1621
+ * @description Retrieves properties from a FormControl, FormArray, or FormGroup.
1622
+ * @summary Gets the FieldProperties associated with a form control from the internal WeakMap.
1623
+ * This method provides access to the original component properties that were used to create
1624
+ * the control, enabling validation, rendering, and behavior configuration.
1625
+ *
1626
+ * @param {FormControl | FormArray | FormGroup} control - The form control to get properties for
1627
+ * @return {FieldProperties} The properties associated with the control, or empty object if not found
1628
+ *
1629
+ * @static
1630
+ * @memberOf NgxFormService
1280
1631
  */
1281
1632
  static getPropsFromControl(control) {
1282
1633
  return this.controls.get(control) || {};
1283
1634
  }
1284
1635
  /**
1285
- * @description Finds a parent element with a specific tag.
1286
- * @summary Traverses up the DOM tree to find the nearest parent element with the specified tag.
1287
- * @param {HTMLElement} el - The starting element.
1288
- * @param {string} tag - The tag name to search for.
1289
- * @return {HTMLElement} The found parent element.
1290
- * @throws {Error} If no parent with the specified tag is found.
1636
+ * @description Finds a parent element with a specific tag in the DOM tree.
1637
+ * @summary Traverses up the DOM tree to find the nearest parent element with the specified tag name.
1638
+ * This is useful for finding container elements or specific parent components in the DOM hierarchy.
1639
+ * The search is case-insensitive for tag name matching.
1640
+ *
1641
+ * @param {HTMLElement} el - The starting element to traverse from
1642
+ * @param {string} tag - The tag name to search for (case-insensitive)
1643
+ * @return {HTMLElement} The found parent element with the specified tag
1644
+ * @throws {Error} If no parent with the specified tag is found in the DOM tree
1645
+ *
1646
+ * @mermaid
1647
+ * sequenceDiagram
1648
+ * participant C as Component
1649
+ * participant NFS as NgxFormService
1650
+ * participant DOM as DOM Tree
1651
+ *
1652
+ * C->>NFS: getParentEl(element, tagName)
1653
+ * loop Traverse up DOM tree
1654
+ * NFS->>DOM: Get parentElement
1655
+ * DOM-->>NFS: Return parent or null
1656
+ * alt Parent exists and tag matches
1657
+ * NFS-->>C: Return parent element
1658
+ * else Parent is null
1659
+ * NFS-->>C: Throw Error
1660
+ * end
1661
+ * end
1662
+ *
1663
+ * @static
1664
+ * @memberOf NgxFormService
1291
1665
  */
1292
1666
  static getParentEl(el, tag) {
1293
1667
  let parent;
@@ -1300,10 +1674,17 @@ class NgxFormService {
1300
1674
  throw new Error(`No parent with the tag ${tag} was found for provided element`);
1301
1675
  }
1302
1676
  /**
1303
- * @description Registers a control with its properties.
1304
- * @summary Associates a control with its properties in the internal WeakMap.
1305
- * @param {AbstractControl} control - The control to register.
1306
- * @param {FieldProperties} props - The properties to associate with the control.
1677
+ * @description Registers a control with its properties in the internal WeakMap.
1678
+ * @summary Associates a form control with its component properties for later retrieval.
1679
+ * This enables the service to maintain metadata about controls without creating memory leaks,
1680
+ * as WeakMap automatically cleans up references when controls are garbage collected.
1681
+ *
1682
+ * @param {AbstractControl} control - The control to register (FormControl, FormGroup, or FormArray)
1683
+ * @param {FieldProperties} props - The properties to associate with the control
1684
+ * @return {void}
1685
+ *
1686
+ * @static
1687
+ * @memberOf NgxFormService
1307
1688
  */
1308
1689
  static register(control, props) {
1309
1690
  this.controls.set(control, props);
@@ -1391,6 +1772,8 @@ class NgxRenderingEngine extends RenderingEngine {
1391
1772
  * @type {string | undefined}
1392
1773
  */
1393
1774
  static { this._operation = undefined; }
1775
+ static { this._projectable = true; }
1776
+ static { this._parentProps = undefined; }
1394
1777
  /**
1395
1778
  * @description Constructs a new NgxRenderingEngine instance
1396
1779
  * @summary Initializes a new instance of the Angular rendering engine by calling the parent
@@ -1459,7 +1842,6 @@ class NgxRenderingEngine extends RenderingEngine {
1459
1842
  const hiddenOn = inputs?.hidden || [];
1460
1843
  if (hiddenOn.includes(operation))
1461
1844
  return { inputs, injector };
1462
- // const hiddenOn = inputs?.hidden || [];
1463
1845
  const result = {
1464
1846
  component,
1465
1847
  inputs,
@@ -1469,6 +1851,8 @@ class NgxRenderingEngine extends RenderingEngine {
1469
1851
  result.inputs['rendererId'] = fieldDef.rendererId;
1470
1852
  // process children
1471
1853
  if (fieldDef.children?.length) {
1854
+ if (!NgxRenderingEngine._parentProps && inputs?.pages)
1855
+ NgxRenderingEngine._parentProps = { pages: inputs?.pages };
1472
1856
  result.children = fieldDef.children.map((child) => {
1473
1857
  if (child?.children?.length) {
1474
1858
  child.children = child.children.filter(c => {
@@ -1477,15 +1861,18 @@ class NgxRenderingEngine extends RenderingEngine {
1477
1861
  return c;
1478
1862
  });
1479
1863
  }
1480
- // create a child form and add its controls as properties of child.props
1481
- NgxFormService.addControlFromProps(registryFormId, child.props, inputs);
1864
+ NgxFormService.addControlFromProps(registryFormId, child.props, { ...inputs, ...NgxRenderingEngine._parentProps || {} });
1482
1865
  return this.fromFieldDefinition(child, vcr, injector, tpl, registryFormId);
1483
1866
  });
1484
1867
  }
1485
1868
  // generating DOM
1869
+ const projectable = NgxRenderingEngine._projectable;
1486
1870
  vcr.clear();
1487
- const template = vcr.createEmbeddedView(tpl, injector).rootNodes;
1488
- const componentInstance = NgxRenderingEngine.createComponent(component, { ...inputs, model: this._model }, componentMetadata, vcr, injector, template);
1871
+ const template = !projectable ? [] : vcr.createEmbeddedView(tpl, injector).rootNodes;
1872
+ const hasChildren = Object.values(possibleInputs).some(({ propName }) => propName === 'children');
1873
+ const hasModel = Object.values(possibleInputs).some(({ propName }) => propName === 'children');
1874
+ const componentInputs = Object.assign(inputs, (hasModel ? { model: this._model } : {}), (hasChildren ? { children: fieldDef?.['children'] || [] } : {}));
1875
+ const componentInstance = NgxRenderingEngine.createComponent(component, componentInputs, componentMetadata, vcr, injector, template);
1489
1876
  result.instance = NgxRenderingEngine._instance = componentInstance.instance;
1490
1877
  return result;
1491
1878
  }
@@ -1533,8 +1920,11 @@ class NgxRenderingEngine extends RenderingEngine {
1533
1920
  *
1534
1921
  * @return {Promise<void>} A promise that resolves when the instance is destroyed
1535
1922
  */
1536
- static async destroy() {
1923
+ static async destroy(formId) {
1924
+ if (formId)
1925
+ NgxFormService.removeRegistry(formId);
1537
1926
  NgxRenderingEngine._instance = undefined;
1927
+ NgxRenderingEngine._parentProps = undefined;
1538
1928
  }
1539
1929
  /**
1540
1930
  * @description Renders a model into an Angular component output
@@ -1566,18 +1956,20 @@ class NgxRenderingEngine extends RenderingEngine {
1566
1956
  * FromField-->>Render: AngularDynamicOutput
1567
1957
  * Render-->>Client: return AngularDynamicOutput
1568
1958
  */
1569
- render(model, globalProps, vcr, injector, tpl) {
1959
+ render(model, globalProps, vcr, injector, tpl, projectable = true) {
1570
1960
  let result;
1571
1961
  try {
1572
1962
  this._model = model;
1963
+ NgxRenderingEngine._projectable = projectable;
1573
1964
  const formId = Date.now().toString(36).toUpperCase();
1574
1965
  const fieldDef = this.toFieldDefinition(model, globalProps);
1575
1966
  const props = fieldDef.props;
1576
1967
  if (!NgxRenderingEngine._operation)
1577
1968
  NgxRenderingEngine._operation = props?.['operation'] || undefined;
1969
+ const formGroup = NgxFormService.createForm(formId, props);
1578
1970
  result = this.fromFieldDefinition(fieldDef, vcr, injector, tpl, formId);
1579
- result.instance['formGroup'] = NgxFormService.getControlFromForm(formId);
1580
- NgxFormService.removeRegistry(formId);
1971
+ result.instance['formGroup'] = formGroup;
1972
+ NgxRenderingEngine.destroy(formId);
1581
1973
  }
1582
1974
  catch (e) {
1583
1975
  throw new InternalError(`Failed to render Model ${model.constructor.name}: ${e}`);
@@ -1774,6 +2166,7 @@ class ComponentRendererComponent {
1774
2166
  * @memberOf ComponentRendererComponent
1775
2167
  */
1776
2168
  this.injector = inject(EnvironmentInjector);
2169
+ this.children = [];
1777
2170
  /**
1778
2171
  * @description Event emitter for events from the rendered component.
1779
2172
  * @summary This output property emits events that originate from the dynamically rendered
@@ -1884,6 +2277,8 @@ class ComponentRendererComponent {
1884
2277
  const props = globals?.['item'] || globals?.['props'] || {};
1885
2278
  if (props?.['tag'])
1886
2279
  delete props['tag'];
2280
+ if (props?.['children'] && !this.children.length)
2281
+ this.children = props['children'];
1887
2282
  const inputKeys = Object.keys(props);
1888
2283
  const unmappedKeys = [];
1889
2284
  for (const input of inputKeys) {
@@ -1896,7 +2291,8 @@ class ComponentRendererComponent {
1896
2291
  }
1897
2292
  }
1898
2293
  this.vcr.clear();
1899
- this.component = NgxRenderingEngine.createComponent(component, props, metadata, this.vcr, this.injector, []);
2294
+ const template = this.children?.length ? this.vcr.createEmbeddedView(this.inner, this.injector).rootNodes : [];
2295
+ this.component = NgxRenderingEngine.createComponent(component, props, metadata, this.vcr, this.injector, template);
1900
2296
  this.subscribeEvents();
1901
2297
  }
1902
2298
  createParentComponent() {
@@ -1987,11 +2383,11 @@ class ComponentRendererComponent {
1987
2383
  }
1988
2384
  }
1989
2385
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1990
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ComponentRendererComponent, isStandalone: true, selector: "ngx-decaf-component-renderer", inputs: { tag: "tag", globals: "globals", model: "model", parent: "parent" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "rendererId" } }, viewQueries: [{ propertyName: "vcr", first: true, predicate: ["componentViewContainer"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }], ngImport: i0, template: "<!-- Keep to avoid id conflicts -->\n<div [id]=\"uid\"></div>\n\n<ng-template #componentViewContainer></ng-template>\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n</ng-template>\n\n\n\n", styles: [""], dependencies: [{ kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "model", "parent"], outputs: ["listenEvent"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }] }); }
2386
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ComponentRendererComponent, isStandalone: true, selector: "ngx-decaf-component-renderer", inputs: { tag: "tag", globals: "globals", children: "children", model: "model", parent: "parent" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "vcr", first: true, predicate: ["componentViewContainer"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }], ngImport: i0, template: "<!-- Keep to avoid id conflicts -->\n<div [id]=\"uid\"></div>\n\n<ng-template #componentViewContainer></ng-template>\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n @if(children?.length) {\n @for(child of children; track child) {\n @if(child.children?.length) {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\" />\n }\n }\n }\n</ng-template>\n\n\n", styles: [""], dependencies: [{ kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "model", "parent"], outputs: ["listenEvent"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }] }); }
1991
2387
  }
1992
2388
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentRendererComponent, decorators: [{
1993
2389
  type: Component,
1994
- args: [{ selector: 'ngx-decaf-component-renderer', imports: [NgComponentOutlet], standalone: true, host: { '[attr.id]': 'rendererId' }, template: "<!-- Keep to avoid id conflicts -->\n<div [id]=\"uid\"></div>\n\n<ng-template #componentViewContainer></ng-template>\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n</ng-template>\n\n\n\n" }]
2390
+ args: [{ selector: 'ngx-decaf-component-renderer', imports: [NgComponentOutlet], standalone: true, host: { '[attr.id]': 'uid' }, template: "<!-- Keep to avoid id conflicts -->\n<div [id]=\"uid\"></div>\n\n<ng-template #componentViewContainer></ng-template>\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n @if(children?.length) {\n @for(child of children; track child) {\n @if(child.children?.length) {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\" />\n }\n }\n }\n</ng-template>\n\n\n" }]
1995
2391
  }], ctorParameters: () => [], propDecorators: { vcr: [{
1996
2392
  type: ViewChild,
1997
2393
  args: ['componentViewContainer', { static: true, read: ViewContainerRef }]
@@ -2000,6 +2396,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2000
2396
  args: [{ required: true }]
2001
2397
  }], globals: [{
2002
2398
  type: Input
2399
+ }], children: [{
2400
+ type: Input
2003
2401
  }], listenEvent: [{
2004
2402
  type: Output
2005
2403
  }], model: [{
@@ -2117,24 +2515,26 @@ class NgxCrudFormField {
2117
2515
  */
2118
2516
  getErrors(parent) {
2119
2517
  const formControl = this.formControl;
2120
- const accordionComponent = parent.closest('ngx-decaf-fieldset')?.querySelector('ion-accordion-group');
2121
- if ((!formControl.pristine || formControl.touched) && !formControl.valid) {
2122
- const errors = Object.keys(formControl.errors ?? {}).map(key => ({
2123
- key: key,
2124
- message: key,
2125
- }));
2126
- if (errors.length) {
2127
- if (accordionComponent && !this.validationErrorEventDispateched) {
2128
- const validationErrorEvent = new CustomEvent(EventConstants.VALIDATION_ERROR, {
2129
- detail: { fieldName: this.name, hasErrors: true },
2130
- bubbles: true
2131
- });
2132
- accordionComponent.dispatchEvent(validationErrorEvent);
2133
- this.validationErrorEventDispateched = true;
2518
+ if (formControl) {
2519
+ const accordionComponent = parent.closest('ngx-decaf-fieldset')?.querySelector('ion-accordion-group');
2520
+ if ((!formControl.pristine || formControl.touched) && !formControl.valid) {
2521
+ const errors = Object.keys(formControl.errors ?? {}).map(key => ({
2522
+ key: key,
2523
+ message: key,
2524
+ }));
2525
+ if (errors.length) {
2526
+ if (accordionComponent && !this.validationErrorEventDispateched) {
2527
+ const validationErrorEvent = new CustomEvent(EventConstants.VALIDATION_ERROR, {
2528
+ detail: { fieldName: this.name, hasErrors: true },
2529
+ bubbles: true
2530
+ });
2531
+ accordionComponent.dispatchEvent(validationErrorEvent);
2532
+ this.validationErrorEventDispateched = true;
2533
+ }
2134
2534
  }
2535
+ for (const error of errors)
2536
+ return `* ${this.sf(this.translateService.instant(`errors.${error?.['message']}`), this[error?.['key']] ?? "")}`;
2135
2537
  }
2136
- for (const error of errors)
2137
- return `* ${this.sf(this.translateService.instant(`errors.${error?.['message']}`), this[error?.['key']] ?? "")}`;
2138
2538
  }
2139
2539
  }
2140
2540
  }
@@ -2248,6 +2648,11 @@ var component = {
2248
2648
  step3: "Type a value",
2249
2649
  no_suggestions: "No suggestions",
2250
2650
  sort: "Sort by"
2651
+ },
2652
+ stepped_form: {
2653
+ next: "Next",
2654
+ previous: "Previous",
2655
+ submit: "Submit"
2251
2656
  }
2252
2657
  };
2253
2658
  var en = {
@@ -2408,6 +2813,15 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxCrudFormField {
2408
2813
  * @memberOf CrudFieldComponent
2409
2814
  */
2410
2815
  this.value = '';
2816
+ /**
2817
+ * @description Whether the field should be hidden.
2818
+ * @summary When true, the field will not be visible in the UI but will still be part of the form model.
2819
+ * This is useful for fields that need to be included in form submission but should not be displayed to the user.
2820
+ *
2821
+ * @type {boolean}
2822
+ * @memberOf CrudFieldComponent
2823
+ */
2824
+ this.hidden = false;
2411
2825
  /**
2412
2826
  * @description Interface style for select inputs.
2413
2827
  * @summary Specifies the interface style for select inputs, such as 'alert', 'action-sheet', or 'popover'.
@@ -2688,7 +3102,7 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxCrudFormField {
2688
3102
  component.dispatchEvent(event);
2689
3103
  }
2690
3104
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CrudFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2691
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", path: "path", childOf: "childOf", type: "type", value: "value", disabled: "disabled", label: "label", placeholder: "placeholder", format: "format", hidden: "hidden", max: "max", maxlength: "maxlength", min: "min", minlength: "minlength", pattern: "pattern", readonly: "readonly", required: "required", step: "step", equals: "equals", different: "different", lessThan: "lessThan", lessThanOrEqual: "lessThanOrEqual", greaterThan: "greaterThan", greaterThanOrEqual: "greaterThanOrEqual", cols: "cols", rows: "rows", alignment: "alignment", checked: "checked", justify: "justify", cancelText: "cancelText", interface: "interface", options: "options", mode: "mode", spellcheck: "spellcheck", inputmode: "inputmode", autocomplete: "autocomplete", fill: "fill", labelPlacement: "labelPlacement", updateOn: "updateOn", formGroup: "formGroup", formControl: "formControl", multiple: "multiple", uid: "uid", translatable: "translatable", activeFormGroup: "activeFormGroup", pk: "pk" }, host: { listeners: { "window:fieldsetAddGroupEvent": "handleFieldsetCreateGroupEvent($event)", "window:fieldsetUpdateGroupEvent": "handleFieldsetUpdateGroupEvent($event)", "window:fieldsetRemoveGroupEvent": "handleFieldsetRemoveGroupEvent($event)" }, properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n <br />\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n <ng-container [formGroup]=\"getActiveFormGroup\">\n <div #container [class]=\"'dcf-input-item ' + (operation || 'create')\" (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\">\n @if(type === 'textarea') {\n <ion-textarea\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [required]=\"required !== undefined ? required : null\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [label]=\"translatable ? (label | translate) : label\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n <ion-item>\n <ion-checkbox\n #checkboxElement\n\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span [innerHTML]=\"label | translate\"></span>\n </ion-checkbox>\n </ion-item>\n }\n @else if(type === 'radio') {\n <ion-radio-group [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track option) {\n <ion-item>\n <ion-radio\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ translatable ? (option?.text | translate) : option?.text }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"translatable ? (label | translate) : label\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @for(option of options; track option.value) {\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n </ion-select>\n }\n @else {\n <ion-input\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [required]=\"required !== undefined ? required : false\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"translatable ? (label | translate) : label\" #component />\n }\n </div>\n </ng-container>\n}\n\n", styles: [".dcf-input-item.create,.dcf-input-item.update{margin-bottom:1.8rem;margin-top:0!important}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)}}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}@media (prefers-color-scheme: light){.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;padding:3px}@media (prefers-color-scheme: light){ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--dcf-color-gray-7)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonRadioGroup, selector: "ion-radio-group", inputs: ["allowEmptySelection", "compareWith", "errorText", "helperText", "name", "value"] }, { kind: "component", type: IonRadio, selector: "ion-radio", inputs: ["alignment", "color", "disabled", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
3105
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", path: "path", childOf: "childOf", type: "type", value: "value", disabled: "disabled", label: "label", placeholder: "placeholder", format: "format", hidden: "hidden", max: "max", maxlength: "maxlength", min: "min", minlength: "minlength", pattern: "pattern", readonly: "readonly", required: "required", step: "step", equals: "equals", different: "different", lessThan: "lessThan", lessThanOrEqual: "lessThanOrEqual", greaterThan: "greaterThan", greaterThanOrEqual: "greaterThanOrEqual", cols: "cols", rows: "rows", alignment: "alignment", checked: "checked", justify: "justify", cancelText: "cancelText", interface: "interface", options: "options", mode: "mode", spellcheck: "spellcheck", inputmode: "inputmode", autocomplete: "autocomplete", fill: "fill", labelPlacement: "labelPlacement", updateOn: "updateOn", formGroup: "formGroup", formControl: "formControl", multiple: "multiple", uid: "uid", page: "page", translatable: "translatable", activeFormGroup: "activeFormGroup", pk: "pk" }, host: { listeners: { "window:fieldsetAddGroupEvent": "handleFieldsetCreateGroupEvent($event)", "window:fieldsetUpdateGroupEvent": "handleFieldsetUpdateGroupEvent($event)", "window:fieldsetRemoveGroupEvent": "handleFieldsetRemoveGroupEvent($event)" }, properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n <br />\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n <ng-container [formGroup]=\"getActiveFormGroup\">\n <div #container [class]=\"'dcf-input-item ' + (operation || 'create')\" (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\">\n @if(type === 'textarea') {\n <ion-textarea\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [required]=\"required !== undefined ? required : null\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n <ion-item>\n <ion-checkbox\n #checkboxElement\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span [innerHTML]=\"label | translate\"></span>\n </ion-checkbox>\n </ion-item>\n }\n @else if(type === 'radio') {\n <ion-radio-group [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track option) {\n <ion-item>\n <ion-radio\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ translatable ? (option?.text | translate) : option?.text }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"translatable ? (label | translate) : label\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @for(option of options; track option.value) {\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n </ion-select>\n }\n @else {\n <ion-input\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [required]=\"required !== undefined ? required : false\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n}\n\n", styles: [".dcf-input-item.create,.dcf-input-item.update{margin-bottom:1.8rem;margin-top:0!important}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)}}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}@media (prefers-color-scheme: light){.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;padding:3px}@media (prefers-color-scheme: light){ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--dcf-color-gray-7)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonRadioGroup, selector: "ion-radio-group", inputs: ["allowEmptySelection", "compareWith", "errorText", "helperText", "name", "value"] }, { kind: "component", type: IonRadio, selector: "ion-radio", inputs: ["alignment", "color", "disabled", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
2692
3106
  };
2693
3107
  CrudFieldComponent = __decorate([
2694
3108
  Dynamic()
@@ -2709,7 +3123,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2709
3123
  IonText,
2710
3124
  IonTextarea,
2711
3125
  IonIcon
2712
- ], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { '[attr.id]': 'uid' }, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n <br />\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n <ng-container [formGroup]=\"getActiveFormGroup\">\n <div #container [class]=\"'dcf-input-item ' + (operation || 'create')\" (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\">\n @if(type === 'textarea') {\n <ion-textarea\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [required]=\"required !== undefined ? required : null\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [label]=\"translatable ? (label | translate) : label\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n <ion-item>\n <ion-checkbox\n #checkboxElement\n\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span [innerHTML]=\"label | translate\"></span>\n </ion-checkbox>\n </ion-item>\n }\n @else if(type === 'radio') {\n <ion-radio-group [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track option) {\n <ion-item>\n <ion-radio\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ translatable ? (option?.text | translate) : option?.text }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"translatable ? (label | translate) : label\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @for(option of options; track option.value) {\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n </ion-select>\n }\n @else {\n <ion-input\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [required]=\"required !== undefined ? required : false\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"translatable ? (placeholder | translate) : placeholder\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"translatable ? (label | translate) : label\" #component />\n }\n </div>\n </ng-container>\n}\n\n", styles: [".dcf-input-item.create,.dcf-input-item.update{margin-bottom:1.8rem;margin-top:0!important}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)}}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}@media (prefers-color-scheme: light){.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;padding:3px}@media (prefers-color-scheme: light){ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--dcf-color-gray-7)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"] }]
3126
+ ], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { '[attr.id]': 'uid' }, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n <br />\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n <ng-container [formGroup]=\"getActiveFormGroup\">\n <div #container [class]=\"'dcf-input-item ' + (operation || 'create')\" (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\">\n @if(type === 'textarea') {\n <ion-textarea\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [required]=\"required !== undefined ? required : null\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n <ion-item>\n <ion-checkbox\n #checkboxElement\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span [innerHTML]=\"label | translate\"></span>\n </ion-checkbox>\n </ion-item>\n }\n @else if(type === 'radio') {\n <ion-radio-group [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track option) {\n <ion-item>\n <ion-radio\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ translatable ? (option?.text | translate) : option?.text }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"translatable ? (label | translate) : label\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @for(option of options; track option.value) {\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n </ion-select>\n }\n @else {\n <ion-input\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [required]=\"required !== undefined ? required : false\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n}\n\n", styles: [".dcf-input-item.create,.dcf-input-item.update{margin-bottom:1.8rem;margin-top:0!important}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)}}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}@media (prefers-color-scheme: light){.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;padding:3px}@media (prefers-color-scheme: light){ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--dcf-color-gray-7)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"] }]
2713
3127
  }], propDecorators: { operation: [{
2714
3128
  type: Input,
2715
3129
  args: [{ required: true }]
@@ -2806,6 +3220,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2806
3220
  type: Input
2807
3221
  }], uid: [{
2808
3222
  type: Input
3223
+ }], page: [{
3224
+ type: Input
2809
3225
  }], translatable: [{
2810
3226
  type: Input
2811
3227
  }], activeFormGroup: [{
@@ -3235,7 +3651,9 @@ class NgxBaseComponent {
3235
3651
  * an initialization message with the component name. This method is typically called
3236
3652
  * during the component's lifecycle setup.
3237
3653
  */
3238
- initialize() {
3654
+ async initialize(parseProps = true, skip) {
3655
+ if (!this.initialized && parseProps)
3656
+ return this.parseProps(this, skip || []);
3239
3657
  this.initialized = true;
3240
3658
  }
3241
3659
  /**
@@ -3315,11 +3733,15 @@ class NgxBaseComponent {
3315
3733
  * @protected
3316
3734
  * @memberOf NgxBaseComponent
3317
3735
  */
3318
- parseProps(instance) {
3736
+ parseProps(instance, skip) {
3319
3737
  Object.keys(instance).forEach((key) => {
3320
- if (Object.keys(this.props).includes(key))
3738
+ if (Object.keys(this.props).includes(key) && !skip.includes(key)) {
3321
3739
  this[key] = this.props[key];
3740
+ delete this.props[key];
3741
+ }
3322
3742
  });
3743
+ if (!this.initialized)
3744
+ this.initialized = true;
3323
3745
  }
3324
3746
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NgxBaseComponent, deps: [{ token: 'instanceToken' }], target: i0.ɵɵFactoryTarget.Component }); }
3325
3747
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: NgxBaseComponent, isStandalone: true, selector: "ng-component", inputs: { rendererId: "rendererId", model: "model", props: "props", item: "item", pk: "pk", route: "route", operations: "operations", uid: "uid", mapper: "mapper", locale: "locale", translatable: "translatable", className: "className", mode: "mode", renderChild: "renderChild" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef, static: true }], usesOnChanges: true, ngImport: i0, template: '<div></div>', isInline: true }); }
@@ -3594,14 +4016,14 @@ let CrudFormComponent = class CrudFormComponent {
3594
4016
  });
3595
4017
  }
3596
4018
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CrudFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3597
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: CrudFormComponent, isStandalone: true, selector: "ngx-decaf-crud-form", inputs: { model: "model", modelId: "modelId", updateOn: "updateOn", target: "target", method: "method", options: "options", action: "action", operation: "operation", handlers: "handlers", formGroup: "formGroup", childOf: "childOf", rendererId: "rendererId", uid: "uid", allowClear: "allowClear" }, outputs: { submitEvent: "submitEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["reactiveForm"], descendants: true, read: ElementRef }], ngImport: i0, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form #reactiveForm [id]=\"rendererId\" [formGroup]=\"formGroup\" (submit)=\"submit($event)\" novalidate [target]=\"target\">\n <ng-content #formContent></ng-content>\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left\">\n <div>\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : options.buttons.submit.text}}\n </ion-button>\n </div>\n @if(!action) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n Back\n </ion-button>\n </div>\n }\n </div>\n </form>\n} @else {\n <div [class]=\"'dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left ' + operation\" [id]=\"uid\">\n @if(operation === OperationKeys.READ && modelId) {\n <div>\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n Delete\n </ion-button>\n </div>\n\n }\n @if(operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n\n <div>\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{options.buttons.submit.text}}\n </ion-button>\n </div>\n }\n\n @if(options.buttons.clear) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ [OperationKeys.DELETE, OperationKeys.READ, OperationKeys.UPDATE].includes(operation) ? 'Back' : options.buttons.clear?.text}}\n </ion-button>\n </div>\n\n }\n </div>\n}\n\n", styles: [".dcf-buttons-container{margin-top:1.8rem;margin-bottom:0}@media (min-width: 768px){.dcf-buttons-container.dcf-flex{flex-direction:row-reverse}}@media (max-width: 767px){.dcf-buttons-container.dcf-flex div{width:100%}.dcf-buttons-container.dcf-flex ion-button{width:100%;margin-bottom:1rem}}form{padding:2rem 1rem}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
4019
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: CrudFormComponent, isStandalone: true, selector: "ngx-decaf-crud-form", inputs: { model: "model", modelId: "modelId", updateOn: "updateOn", target: "target", method: "method", options: "options", action: "action", operation: "operation", handlers: "handlers", formGroup: "formGroup", childOf: "childOf", rendererId: "rendererId", uid: "uid", allowClear: "allowClear" }, outputs: { submitEvent: "submitEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["reactiveForm"], descendants: true, read: ElementRef }], ngImport: i0, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form #reactiveForm [id]=\"rendererId\" [formGroup]=\"formGroup\" (submit)=\"submit($event)\" novalidate [target]=\"target\">\n <ng-content #formContent></ng-content>\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left\">\n <div>\n <ion-button type=\"submit\" [expand]=\"action ? 'block' : 'default'\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : options.buttons.submit.text}}\n </ion-button>\n </div>\n @if(!action) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n Back\n </ion-button>\n </div>\n }\n </div>\n </form>\n} @else {\n <div [class]=\"'dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left ' + operation\" [id]=\"uid\">\n @if(operation === OperationKeys.READ && modelId) {\n <div>\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n Delete\n </ion-button>\n </div>\n\n }\n @if(operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n\n <div>\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{options.buttons.submit.text}}\n </ion-button>\n </div>\n }\n\n @if(options.buttons.clear) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ [OperationKeys.DELETE, OperationKeys.READ, OperationKeys.UPDATE].includes(operation) ? 'Back' : options.buttons.clear?.text}}\n </ion-button>\n </div>\n\n }\n </div>\n}\n\n", styles: [".dcf-buttons-container{margin-top:1.8rem;margin-bottom:0}@media (min-width: 768px){.dcf-buttons-container.dcf-flex{flex-direction:row-reverse}}@media (max-width: 767px){.dcf-buttons-container.dcf-flex div{width:100%}.dcf-buttons-container.dcf-flex ion-button{width:100%;margin-bottom:1rem}}form{padding:2rem 1rem}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
3598
4020
  };
3599
4021
  CrudFormComponent = __decorate([
3600
4022
  Dynamic()
3601
4023
  ], CrudFormComponent);
3602
4024
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CrudFormComponent, decorators: [{
3603
4025
  type: Component,
3604
- args: [{ standalone: true, selector: 'ngx-decaf-crud-form', imports: [ReactiveFormsModule, IonButton, IonIcon], host: { '[attr.id]': 'uid' }, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form #reactiveForm [id]=\"rendererId\" [formGroup]=\"formGroup\" (submit)=\"submit($event)\" novalidate [target]=\"target\">\n <ng-content #formContent></ng-content>\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left\">\n <div>\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : options.buttons.submit.text}}\n </ion-button>\n </div>\n @if(!action) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n Back\n </ion-button>\n </div>\n }\n </div>\n </form>\n} @else {\n <div [class]=\"'dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left ' + operation\" [id]=\"uid\">\n @if(operation === OperationKeys.READ && modelId) {\n <div>\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n Delete\n </ion-button>\n </div>\n\n }\n @if(operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n\n <div>\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{options.buttons.submit.text}}\n </ion-button>\n </div>\n }\n\n @if(options.buttons.clear) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ [OperationKeys.DELETE, OperationKeys.READ, OperationKeys.UPDATE].includes(operation) ? 'Back' : options.buttons.clear?.text}}\n </ion-button>\n </div>\n\n }\n </div>\n}\n\n", styles: [".dcf-buttons-container{margin-top:1.8rem;margin-bottom:0}@media (min-width: 768px){.dcf-buttons-container.dcf-flex{flex-direction:row-reverse}}@media (max-width: 767px){.dcf-buttons-container.dcf-flex div{width:100%}.dcf-buttons-container.dcf-flex ion-button{width:100%;margin-bottom:1rem}}form{padding:2rem 1rem}\n"] }]
4026
+ args: [{ standalone: true, selector: 'ngx-decaf-crud-form', imports: [ReactiveFormsModule, IonButton, IonIcon], host: { '[attr.id]': 'uid' }, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form #reactiveForm [id]=\"rendererId\" [formGroup]=\"formGroup\" (submit)=\"submit($event)\" novalidate [target]=\"target\">\n <ng-content #formContent></ng-content>\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left\">\n <div>\n <ion-button type=\"submit\" [expand]=\"action ? 'block' : 'default'\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{ action ? action : options.buttons.submit.text}}\n </ion-button>\n </div>\n @if(!action) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n Back\n </ion-button>\n </div>\n }\n </div>\n </form>\n} @else {\n <div [class]=\"'dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left ' + operation\" [id]=\"uid\">\n @if(operation === OperationKeys.READ && modelId) {\n <div>\n <ion-button\n (click)=\"handleDelete()\"\n color=\"danger\"\n type=\"button\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n Delete\n </ion-button>\n </div>\n\n }\n @if(operation === OperationKeys.CREATE || operation === OperationKeys.UPDATE) {\n\n <div>\n <ion-button\n type=\"submit\">\n @if(options.buttons.submit.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.submit.iconSlot\" [name]=\"options.buttons.submit.icon\"></ion-icon>\n }\n {{options.buttons.submit.text}}\n </ion-button>\n </div>\n }\n\n @if(options.buttons.clear) {\n <div>\n <ion-button fill=\"clear\" (click)=\"handleReset()\">\n @if(options.buttons.clear?.icon) {\n <ion-icon aria-hidden=\"true\" [slot]=\"options.buttons.clear?.iconSlot\" [name]=\"options.buttons.clear?.icon\"></ion-icon>\n }\n {{ [OperationKeys.DELETE, OperationKeys.READ, OperationKeys.UPDATE].includes(operation) ? 'Back' : options.buttons.clear?.text}}\n </ion-button>\n </div>\n\n }\n </div>\n}\n\n", styles: [".dcf-buttons-container{margin-top:1.8rem;margin-bottom:0}@media (min-width: 768px){.dcf-buttons-container.dcf-flex{flex-direction:row-reverse}}@media (max-width: 767px){.dcf-buttons-container.dcf-flex div{width:100%}.dcf-buttons-container.dcf-flex ion-button{width:100%;margin-bottom:1rem}}form{padding:2rem 1rem}\n"] }]
3605
4027
  }], propDecorators: { model: [{
3606
4028
  type: Input
3607
4029
  }], modelId: [{
@@ -3842,7 +4264,7 @@ let EmptyStateComponent = class EmptyStateComponent extends NgxBaseComponent {
3842
4264
  * @memberOf EmptyStateComponent
3843
4265
  */
3844
4266
  async ngOnInit() {
3845
- this.parseProps(this);
4267
+ this.initialize();
3846
4268
  this.translatable = stringToBoolean(this.translatable);
3847
4269
  this.showIcon = stringToBoolean(this.showIcon);
3848
4270
  this.locale = this.getLocale(this.translatable);
@@ -4580,7 +5002,7 @@ let FieldsetComponent = class FieldsetComponent extends NgxBaseComponent {
4580
5002
  return this.mapper;
4581
5003
  }
4582
5004
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4583
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: FieldsetComponent, isStandalone: true, selector: "ngx-decaf-fieldset", inputs: { name: "name", childOf: "childOf", uid: "uid", customTypes: "customTypes", operation: "operation", formGroup: "formGroup", title: "title", description: "description", target: "target", multiple: "multiple", value: "value", handlers: "handlers" }, host: { properties: { "attr.id": "overriode " } }, viewQueries: [{ propertyName: "accordionComponent", first: true, predicate: ["accordionComponent"], descendants: true }], usesInheritance: true, ngImport: i0, template: "\n\n<fieldset\n (fieldsetAddGroupEvent)=\"handleCreateItem($event)\"\n (fieldsetRemoveGroupEvent)=\"handleRemoveItem(undefined, $event)\"\n [class]=\"'dcf-fieldset ' + operation\"\n #component>\n <ion-accordion-group [class.open]=\"isOpen\" [class.hasValidationErrors]=\"hasValidationErrors\" (validationErrorEvent)=\"handleValidationError($event)\" #accordionComponent>\n <ion-accordion value=\"open\">\n <ion-item slot=\"header\" (click)=\"handleAccordionToggle($event)\">\n <div class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-width-1-1\">\n <div class=\"dcf-width-expand\">\n <legend>{{ name | translate }}</legend>\n </div>\n @if(!isRequired && ['create', 'update'].includes(operation)) {\n <div class=\"dcf-width-auto dcf-delete\">\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleRemoveComponent($event)\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </div>\n }\n </div>\n </ion-item>\n <div slot=\"content\" [attr.aria-hidden]=\"!isOpen\">\n @if(multiple && items.length) {\n <ion-list class=\"dcf-fields-list\">\n <ion-reorder-group [formGroup]=\"formGroup.parent\" [disabled]=\"updatingItem\" (ionItemReorder)=\"handleReorderItems($any($event))\" #accordionComponent>\n @for(item of items; track item.index) {\n <ion-item [class.not-unique]=\"item.title === isUniqueError\" [class.updating]=\"updatingItem?.[pk] === item.title\" lines=\"full\" [button]=\"false\">\n @if(items?.length > 1 && !updatingItem) {\n <ion-reorder slot=\"start\">\n <ion-icon name=\"swap-vertical-outline\"></ion-icon>\n </ion-reorder>\n } @else {\n <div slot=\"start\">\n <ion-icon class=\"dcf-reorder-disabled\" size=\"small\" name=\"swap-vertical-outline\" disabled></ion-icon>\n </div>\n }\n <ion-label [color]=\"(item.title === isUniqueError && !updatingItem?.[pk] === item.title) ? 'danger' : ''\">{{ item.index }}. {{ item.title }}\n @if(item.description?.length > 0) {\n <br />\n <ion-text class=\"dcf-subtitle\">{{item.description}}</ion-text>\n }\n </ion-label>\n @if(!updatingItem || updatingItem?.[pk] !== item.title) {\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleUpdateItem(item.title, $index)\">\n <ion-icon name=\"create-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n }\n\n @if(!updatingItem) {\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleRemoveItem(item.title)\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n }\n </ion-item>\n }\n </ion-reorder-group>\n </ion-list>\n }\n\n <ng-content></ng-content>\n\n @if(multiple && ['create', 'update'].includes(operation)) {\n @if(isUniqueError) {\n <div class=\"dcf-not-unique-container dcf-animation dcf-animation-bottom-small dcf-animation-fast\">\n <div class=\" dcf-grid dcf-grid-collapse dcf-width-1-1 \">\n <div class=\"dcf-auto\" [attr.style]=\"'max-width: 50px'\">\n <ion-icon name=\"alert-circle-outline\"></ion-icon>\n </div>\n <div class=\"dcf-width-expand\">\n <ion-text color=\"danger\" class=\"dcf-text-small\">{{ locale + '.not_unique' | translate : { value: isUniqueError } }}</ion-text>\n </div>\n </div>\n </div>\n }\n <div class=\"dcf-margin-bottom dcf-grid dcf-grid-collapse dcf-flex\">\n @if(updatingItem) {\n <ion-button size=\"small\" fill=\"clear\" color=\"danger\" (click)=\"handleCancelUpdateItem()\">\n {{ buttonCancelLabel }}\n </ion-button>\n }\n <ion-button size=\"small\" fill=\"clear\" class=\"dcf-button-add\" (click)=\"handleCreateItem()\">\n <ion-icon name=\"add-outline\" slot=\"start\"></ion-icon>\n {{buttonLabel}}\n </ion-button>\n\n </div>\n }\n\n </div>\n </ion-accordion>\n </ion-accordion-group>\n</fieldset>\n\n", styles: ["ion-accordion-group ion-item[slot=header] .dcf-delete{width:30px}ion-accordion-group ion-item[slot=header] .dcf-delete ion-button{transform:translateY(-2px)}ion-accordion-group ion-item[slot=header] .dcf-delete ion-icon{font-size:1.15rem}::ng-deep ion-accordion ngx-decaf-crud-field:last-child ion-item{--inner-border-width: 0px !important;--border-width: 0px !important}.dcf-fieldset{margin-bottom:1.8rem;margin-top:1rem;padding-bottom:0;padding-top:1rem;background:var(--dcf-card-background);border-radius:6px;height:100%}@media (prefers-color-scheme: light){.dcf-fieldset{border:1px solid var(--dcf-color-gray-3)}.dcf-fieldset .dcf-button-add{color:var(--ion-color-dark)!important}}@media (prefers-color-scheme: dark){.dcf-fieldset{border:1px solid var(--dcf-color-step-400)}.dcf-fieldset .dcf-button-add{color:var(--ion-color-gray-2)}}.dcf-fieldset.read,.dcf-fieldset.delete{margin-top:1.25rem;padding-bottom:1rem}.dcf-fieldset.read [slot=content],.dcf-fieldset.delete [slot=content]{padding-top:0!important}.dcf-fieldset.read ion-accordion,.dcf-fieldset.delete ion-accordion{margin-bottom:0rem!important}@media (prefers-color-scheme: dark){.dcf-fieldset.read,.dcf-fieldset.delete{border:1px solid var(--dcf-color-gray-6)}}.dcf-fieldset ion-accordion{background:var(--dcf-card-background);margin-bottom:1rem}.dcf-fieldset ion-accordion.accordion-collapsing,.dcf-fieldset ion-accordion.accordion-collapsed{margin-bottom:1rem}.dcf-fieldset ion-accordion ion-item[slot=header]{--border-color: transparent;--border-radius: 6px;--inner-border-width: 0;--padding-start: 12px}.dcf-fieldset ion-accordion ion-item[slot=header] legend{font-weight:600;font-size:1rem;margin:0}@media (prefers-color-scheme: light){.dcf-fieldset ion-accordion ion-item[slot=header] legend{color:var(--dcf-color-gray-7)}}.dcf-fieldset ion-accordion [slot=content]{padding-top:2rem!important;padding-inline:.75rem}.dcf-not-unique-container{display:flex;justify-content:center;align-items:center;margin-bottom:1rem;flex:1 1 auto}.dcf-not-unique-container>div{display:flex;justify-content:center;align-items:center}.dcf-not-unique-container ion-icon{transform:translatey(2px);margin-right:5px}.dcf-fields-list{margin-top:-1rem;margin-bottom:1rem;padding:0!important}.dcf-fields-list ion-item{--min-height: 50px;--padding-top: .25rem;--padding-bottom: .25rem;--padding-start: .75rem;--padding-end: .75rem;--inner-padding-start: 0px !important;--inner-padding-end: 0px !important;--border-color: var(--dcf-color-gray-2) !important;border:1px solid transparent;box-sizing:border-box}.dcf-fields-list ion-item ion-icon.dcf-reorder-disabled{width:1rem;transform:translatey(2px);color:var(--dcf-color-gray-4)}.dcf-fields-list ion-item.updating{--background: rgba(var(--dcf-color-primary-rgb), .1) !important}.dcf-fields-list ion-item.not-unique{--background: rgba(var(--dcf-color-danger-rgb), .05) !important}.dcf-fields-list ion-item .dcf-subtitle{font-size:.8rem;color:var(--dcf-color-gray-7)}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: IonAccordionGroup, selector: "ion-accordion-group", inputs: ["animated", "disabled", "expand", "mode", "multiple", "readonly", "value"] }, { kind: "component", type: IonAccordion, selector: "ion-accordion", inputs: ["disabled", "mode", "readonly", "toggleIcon", "toggleIconSlot", "value"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonReorder, selector: "ion-reorder" }, { kind: "component", type: IonReorderGroup, selector: "ion-reorder-group", inputs: ["disabled"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
5005
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: FieldsetComponent, isStandalone: true, selector: "ngx-decaf-fieldset", inputs: { name: "name", childOf: "childOf", page: "page", uid: "uid", customTypes: "customTypes", operation: "operation", formGroup: "formGroup", title: "title", description: "description", target: "target", multiple: "multiple", value: "value", handlers: "handlers" }, host: { properties: { "attr.id": "overriode " } }, viewQueries: [{ propertyName: "accordionComponent", first: true, predicate: ["accordionComponent"], descendants: true }], usesInheritance: true, ngImport: i0, template: "\n\n<fieldset\n (fieldsetAddGroupEvent)=\"handleCreateItem($event)\"\n (fieldsetRemoveGroupEvent)=\"handleRemoveItem(undefined, $event)\"\n [class]=\"'dcf-fieldset ' + operation\"\n #component>\n <ion-accordion-group [class.open]=\"isOpen\" [class.hasValidationErrors]=\"hasValidationErrors\" (validationErrorEvent)=\"handleValidationError($event)\" #accordionComponent>\n <ion-accordion value=\"open\">\n <ion-item slot=\"header\" (click)=\"handleAccordionToggle($event)\">\n <div class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-width-1-1\">\n <div class=\"dcf-width-expand\">\n <legend>{{ name | translate }}</legend>\n </div>\n @if(!isRequired && ['create', 'update'].includes(operation)) {\n <div class=\"dcf-width-auto dcf-delete\">\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleRemoveComponent($event)\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </div>\n }\n </div>\n </ion-item>\n <div slot=\"content\" [attr.aria-hidden]=\"!isOpen\">\n @if(multiple && items.length) {\n <ion-list class=\"dcf-fields-list\">\n <ion-reorder-group [formGroup]=\"formGroup.parent\" [disabled]=\"updatingItem\" (ionItemReorder)=\"handleReorderItems($any($event))\" #accordionComponent>\n @for(item of items; track item.index) {\n <ion-item [class.not-unique]=\"item.title === isUniqueError\" [class.updating]=\"updatingItem?.[pk] === item.title\" lines=\"full\" [button]=\"false\">\n @if(items?.length > 1 && !updatingItem) {\n <ion-reorder slot=\"start\">\n <ion-icon name=\"swap-vertical-outline\"></ion-icon>\n </ion-reorder>\n } @else {\n <div slot=\"start\">\n <ion-icon class=\"dcf-reorder-disabled\" size=\"small\" name=\"swap-vertical-outline\" disabled></ion-icon>\n </div>\n }\n <ion-label [color]=\"(item.title === isUniqueError && !updatingItem?.[pk] === item.title) ? 'danger' : ''\">{{ item.index }}. {{ item.title }}\n @if(item.description?.length > 0) {\n <br />\n <ion-text class=\"dcf-subtitle\">{{item.description}}</ion-text>\n }\n </ion-label>\n @if(!updatingItem || updatingItem?.[pk] !== item.title) {\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleUpdateItem(item.title, $index)\">\n <ion-icon name=\"create-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n }\n\n @if(!updatingItem) {\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleRemoveItem(item.title)\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n }\n </ion-item>\n }\n </ion-reorder-group>\n </ion-list>\n }\n\n <ng-content select=\"[slot=content]\"></ng-content>\n\n @if(multiple && ['create', 'update'].includes(operation)) {\n @if(isUniqueError) {\n <div class=\"dcf-not-unique-container dcf-animation dcf-animation-bottom-small dcf-animation-fast\">\n <div class=\" dcf-grid dcf-grid-collapse dcf-width-1-1 \">\n <div class=\"dcf-auto\" [attr.style]=\"'max-width: 50px'\">\n <ion-icon name=\"alert-circle-outline\"></ion-icon>\n </div>\n <div class=\"dcf-width-expand\">\n <ion-text color=\"danger\" class=\"dcf-text-small\">{{ locale + '.not_unique' | translate : { value: isUniqueError } }}</ion-text>\n </div>\n </div>\n </div>\n }\n <div class=\"dcf-margin-bottom dcf-grid dcf-grid-collapse dcf-flex\">\n @if(updatingItem) {\n <ion-button size=\"small\" fill=\"clear\" color=\"danger\" (click)=\"handleCancelUpdateItem()\">\n {{ buttonCancelLabel }}\n </ion-button>\n }\n <ion-button size=\"small\" fill=\"clear\" class=\"dcf-button-add\" (click)=\"handleCreateItem()\">\n <ion-icon name=\"add-outline\" slot=\"start\"></ion-icon>\n {{buttonLabel}}\n </ion-button>\n\n </div>\n }\n\n </div>\n </ion-accordion>\n </ion-accordion-group>\n</fieldset>\n\n", styles: ["ion-accordion-group ion-item[slot=header] .dcf-delete{width:30px}ion-accordion-group ion-item[slot=header] .dcf-delete ion-button{transform:translateY(-2px)}ion-accordion-group ion-item[slot=header] .dcf-delete ion-icon{font-size:1.15rem}::ng-deep ion-accordion ngx-decaf-crud-field:last-child ion-item{--inner-border-width: 0px !important;--border-width: 0px !important}.dcf-fieldset{margin-bottom:1.8rem;margin-top:1rem;padding-bottom:0;padding-top:1rem;background:var(--dcf-card-background);border-radius:6px;height:100%}@media (prefers-color-scheme: light){.dcf-fieldset{border:1px solid var(--dcf-color-gray-3)}.dcf-fieldset .dcf-button-add{color:var(--ion-color-dark)!important}}@media (prefers-color-scheme: dark){.dcf-fieldset{border:1px solid var(--dcf-color-step-400)}.dcf-fieldset .dcf-button-add{color:var(--ion-color-gray-2)}}.dcf-fieldset.read,.dcf-fieldset.delete{margin-top:1.25rem;padding-bottom:1rem}.dcf-fieldset.read [slot=content],.dcf-fieldset.delete [slot=content]{padding-top:0!important}.dcf-fieldset.read ion-accordion,.dcf-fieldset.delete ion-accordion{margin-bottom:0rem!important}@media (prefers-color-scheme: dark){.dcf-fieldset.read,.dcf-fieldset.delete{border:1px solid var(--dcf-color-gray-6)}}.dcf-fieldset ion-accordion{background:var(--dcf-card-background);margin-bottom:1rem}.dcf-fieldset ion-accordion.accordion-collapsing,.dcf-fieldset ion-accordion.accordion-collapsed{margin-bottom:1rem}.dcf-fieldset ion-accordion ion-item[slot=header]{--border-color: transparent;--border-radius: 6px;--inner-border-width: 0;--padding-start: 12px}.dcf-fieldset ion-accordion ion-item[slot=header] legend{font-weight:600;font-size:1rem;margin:0}@media (prefers-color-scheme: light){.dcf-fieldset ion-accordion ion-item[slot=header] legend{color:var(--dcf-color-gray-7)}}.dcf-fieldset ion-accordion [slot=content]{padding-top:2rem!important;padding-inline:.75rem}.dcf-not-unique-container{display:flex;justify-content:center;align-items:center;margin-bottom:1rem;flex:1 1 auto}.dcf-not-unique-container>div{display:flex;justify-content:center;align-items:center}.dcf-not-unique-container ion-icon{transform:translatey(2px);margin-right:5px}.dcf-fields-list{margin-top:-1rem;margin-bottom:1rem;padding:0!important}.dcf-fields-list ion-item{--min-height: 50px;--padding-top: .25rem;--padding-bottom: .25rem;--padding-start: .75rem;--padding-end: .75rem;--inner-padding-start: 0px !important;--inner-padding-end: 0px !important;--border-color: var(--dcf-color-gray-2) !important;border:1px solid transparent;box-sizing:border-box}.dcf-fields-list ion-item ion-icon.dcf-reorder-disabled{width:1rem;transform:translatey(2px);color:var(--dcf-color-gray-4)}.dcf-fields-list ion-item.updating{--background: rgba(var(--dcf-color-primary-rgb), .1) !important}.dcf-fields-list ion-item.not-unique{--background: rgba(var(--dcf-color-danger-rgb), .05) !important}.dcf-fields-list ion-item .dcf-subtitle{font-size:.8rem;color:var(--dcf-color-gray-7)}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: IonAccordionGroup, selector: "ion-accordion-group", inputs: ["animated", "disabled", "expand", "mode", "multiple", "readonly", "value"] }, { kind: "component", type: IonAccordion, selector: "ion-accordion", inputs: ["disabled", "mode", "readonly", "toggleIcon", "toggleIconSlot", "value"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonReorder, selector: "ion-reorder" }, { kind: "component", type: IonReorderGroup, selector: "ion-reorder-group", inputs: ["disabled"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
4584
5006
  };
4585
5007
  FieldsetComponent = __decorate([
4586
5008
  Dynamic(),
@@ -4601,7 +5023,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4601
5023
  IonReorderGroup,
4602
5024
  IonButton,
4603
5025
  IonIcon,
4604
- ], host: { '[attr.id]': 'overriode ' }, template: "\n\n<fieldset\n (fieldsetAddGroupEvent)=\"handleCreateItem($event)\"\n (fieldsetRemoveGroupEvent)=\"handleRemoveItem(undefined, $event)\"\n [class]=\"'dcf-fieldset ' + operation\"\n #component>\n <ion-accordion-group [class.open]=\"isOpen\" [class.hasValidationErrors]=\"hasValidationErrors\" (validationErrorEvent)=\"handleValidationError($event)\" #accordionComponent>\n <ion-accordion value=\"open\">\n <ion-item slot=\"header\" (click)=\"handleAccordionToggle($event)\">\n <div class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-width-1-1\">\n <div class=\"dcf-width-expand\">\n <legend>{{ name | translate }}</legend>\n </div>\n @if(!isRequired && ['create', 'update'].includes(operation)) {\n <div class=\"dcf-width-auto dcf-delete\">\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleRemoveComponent($event)\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </div>\n }\n </div>\n </ion-item>\n <div slot=\"content\" [attr.aria-hidden]=\"!isOpen\">\n @if(multiple && items.length) {\n <ion-list class=\"dcf-fields-list\">\n <ion-reorder-group [formGroup]=\"formGroup.parent\" [disabled]=\"updatingItem\" (ionItemReorder)=\"handleReorderItems($any($event))\" #accordionComponent>\n @for(item of items; track item.index) {\n <ion-item [class.not-unique]=\"item.title === isUniqueError\" [class.updating]=\"updatingItem?.[pk] === item.title\" lines=\"full\" [button]=\"false\">\n @if(items?.length > 1 && !updatingItem) {\n <ion-reorder slot=\"start\">\n <ion-icon name=\"swap-vertical-outline\"></ion-icon>\n </ion-reorder>\n } @else {\n <div slot=\"start\">\n <ion-icon class=\"dcf-reorder-disabled\" size=\"small\" name=\"swap-vertical-outline\" disabled></ion-icon>\n </div>\n }\n <ion-label [color]=\"(item.title === isUniqueError && !updatingItem?.[pk] === item.title) ? 'danger' : ''\">{{ item.index }}. {{ item.title }}\n @if(item.description?.length > 0) {\n <br />\n <ion-text class=\"dcf-subtitle\">{{item.description}}</ion-text>\n }\n </ion-label>\n @if(!updatingItem || updatingItem?.[pk] !== item.title) {\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleUpdateItem(item.title, $index)\">\n <ion-icon name=\"create-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n }\n\n @if(!updatingItem) {\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleRemoveItem(item.title)\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n }\n </ion-item>\n }\n </ion-reorder-group>\n </ion-list>\n }\n\n <ng-content></ng-content>\n\n @if(multiple && ['create', 'update'].includes(operation)) {\n @if(isUniqueError) {\n <div class=\"dcf-not-unique-container dcf-animation dcf-animation-bottom-small dcf-animation-fast\">\n <div class=\" dcf-grid dcf-grid-collapse dcf-width-1-1 \">\n <div class=\"dcf-auto\" [attr.style]=\"'max-width: 50px'\">\n <ion-icon name=\"alert-circle-outline\"></ion-icon>\n </div>\n <div class=\"dcf-width-expand\">\n <ion-text color=\"danger\" class=\"dcf-text-small\">{{ locale + '.not_unique' | translate : { value: isUniqueError } }}</ion-text>\n </div>\n </div>\n </div>\n }\n <div class=\"dcf-margin-bottom dcf-grid dcf-grid-collapse dcf-flex\">\n @if(updatingItem) {\n <ion-button size=\"small\" fill=\"clear\" color=\"danger\" (click)=\"handleCancelUpdateItem()\">\n {{ buttonCancelLabel }}\n </ion-button>\n }\n <ion-button size=\"small\" fill=\"clear\" class=\"dcf-button-add\" (click)=\"handleCreateItem()\">\n <ion-icon name=\"add-outline\" slot=\"start\"></ion-icon>\n {{buttonLabel}}\n </ion-button>\n\n </div>\n }\n\n </div>\n </ion-accordion>\n </ion-accordion-group>\n</fieldset>\n\n", styles: ["ion-accordion-group ion-item[slot=header] .dcf-delete{width:30px}ion-accordion-group ion-item[slot=header] .dcf-delete ion-button{transform:translateY(-2px)}ion-accordion-group ion-item[slot=header] .dcf-delete ion-icon{font-size:1.15rem}::ng-deep ion-accordion ngx-decaf-crud-field:last-child ion-item{--inner-border-width: 0px !important;--border-width: 0px !important}.dcf-fieldset{margin-bottom:1.8rem;margin-top:1rem;padding-bottom:0;padding-top:1rem;background:var(--dcf-card-background);border-radius:6px;height:100%}@media (prefers-color-scheme: light){.dcf-fieldset{border:1px solid var(--dcf-color-gray-3)}.dcf-fieldset .dcf-button-add{color:var(--ion-color-dark)!important}}@media (prefers-color-scheme: dark){.dcf-fieldset{border:1px solid var(--dcf-color-step-400)}.dcf-fieldset .dcf-button-add{color:var(--ion-color-gray-2)}}.dcf-fieldset.read,.dcf-fieldset.delete{margin-top:1.25rem;padding-bottom:1rem}.dcf-fieldset.read [slot=content],.dcf-fieldset.delete [slot=content]{padding-top:0!important}.dcf-fieldset.read ion-accordion,.dcf-fieldset.delete ion-accordion{margin-bottom:0rem!important}@media (prefers-color-scheme: dark){.dcf-fieldset.read,.dcf-fieldset.delete{border:1px solid var(--dcf-color-gray-6)}}.dcf-fieldset ion-accordion{background:var(--dcf-card-background);margin-bottom:1rem}.dcf-fieldset ion-accordion.accordion-collapsing,.dcf-fieldset ion-accordion.accordion-collapsed{margin-bottom:1rem}.dcf-fieldset ion-accordion ion-item[slot=header]{--border-color: transparent;--border-radius: 6px;--inner-border-width: 0;--padding-start: 12px}.dcf-fieldset ion-accordion ion-item[slot=header] legend{font-weight:600;font-size:1rem;margin:0}@media (prefers-color-scheme: light){.dcf-fieldset ion-accordion ion-item[slot=header] legend{color:var(--dcf-color-gray-7)}}.dcf-fieldset ion-accordion [slot=content]{padding-top:2rem!important;padding-inline:.75rem}.dcf-not-unique-container{display:flex;justify-content:center;align-items:center;margin-bottom:1rem;flex:1 1 auto}.dcf-not-unique-container>div{display:flex;justify-content:center;align-items:center}.dcf-not-unique-container ion-icon{transform:translatey(2px);margin-right:5px}.dcf-fields-list{margin-top:-1rem;margin-bottom:1rem;padding:0!important}.dcf-fields-list ion-item{--min-height: 50px;--padding-top: .25rem;--padding-bottom: .25rem;--padding-start: .75rem;--padding-end: .75rem;--inner-padding-start: 0px !important;--inner-padding-end: 0px !important;--border-color: var(--dcf-color-gray-2) !important;border:1px solid transparent;box-sizing:border-box}.dcf-fields-list ion-item ion-icon.dcf-reorder-disabled{width:1rem;transform:translatey(2px);color:var(--dcf-color-gray-4)}.dcf-fields-list ion-item.updating{--background: rgba(var(--dcf-color-primary-rgb), .1) !important}.dcf-fields-list ion-item.not-unique{--background: rgba(var(--dcf-color-danger-rgb), .05) !important}.dcf-fields-list ion-item .dcf-subtitle{font-size:.8rem;color:var(--dcf-color-gray-7)}\n"] }]
5026
+ ], host: { '[attr.id]': 'overriode ' }, template: "\n\n<fieldset\n (fieldsetAddGroupEvent)=\"handleCreateItem($event)\"\n (fieldsetRemoveGroupEvent)=\"handleRemoveItem(undefined, $event)\"\n [class]=\"'dcf-fieldset ' + operation\"\n #component>\n <ion-accordion-group [class.open]=\"isOpen\" [class.hasValidationErrors]=\"hasValidationErrors\" (validationErrorEvent)=\"handleValidationError($event)\" #accordionComponent>\n <ion-accordion value=\"open\">\n <ion-item slot=\"header\" (click)=\"handleAccordionToggle($event)\">\n <div class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-width-1-1\">\n <div class=\"dcf-width-expand\">\n <legend>{{ name | translate }}</legend>\n </div>\n @if(!isRequired && ['create', 'update'].includes(operation)) {\n <div class=\"dcf-width-auto dcf-delete\">\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleRemoveComponent($event)\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </div>\n }\n </div>\n </ion-item>\n <div slot=\"content\" [attr.aria-hidden]=\"!isOpen\">\n @if(multiple && items.length) {\n <ion-list class=\"dcf-fields-list\">\n <ion-reorder-group [formGroup]=\"formGroup.parent\" [disabled]=\"updatingItem\" (ionItemReorder)=\"handleReorderItems($any($event))\" #accordionComponent>\n @for(item of items; track item.index) {\n <ion-item [class.not-unique]=\"item.title === isUniqueError\" [class.updating]=\"updatingItem?.[pk] === item.title\" lines=\"full\" [button]=\"false\">\n @if(items?.length > 1 && !updatingItem) {\n <ion-reorder slot=\"start\">\n <ion-icon name=\"swap-vertical-outline\"></ion-icon>\n </ion-reorder>\n } @else {\n <div slot=\"start\">\n <ion-icon class=\"dcf-reorder-disabled\" size=\"small\" name=\"swap-vertical-outline\" disabled></ion-icon>\n </div>\n }\n <ion-label [color]=\"(item.title === isUniqueError && !updatingItem?.[pk] === item.title) ? 'danger' : ''\">{{ item.index }}. {{ item.title }}\n @if(item.description?.length > 0) {\n <br />\n <ion-text class=\"dcf-subtitle\">{{item.description}}</ion-text>\n }\n </ion-label>\n @if(!updatingItem || updatingItem?.[pk] !== item.title) {\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleUpdateItem(item.title, $index)\">\n <ion-icon name=\"create-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n }\n\n @if(!updatingItem) {\n <ion-button fill=\"clear\" size=\"small\" (click)=\"handleRemoveItem(item.title)\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n }\n </ion-item>\n }\n </ion-reorder-group>\n </ion-list>\n }\n\n <ng-content select=\"[slot=content]\"></ng-content>\n\n @if(multiple && ['create', 'update'].includes(operation)) {\n @if(isUniqueError) {\n <div class=\"dcf-not-unique-container dcf-animation dcf-animation-bottom-small dcf-animation-fast\">\n <div class=\" dcf-grid dcf-grid-collapse dcf-width-1-1 \">\n <div class=\"dcf-auto\" [attr.style]=\"'max-width: 50px'\">\n <ion-icon name=\"alert-circle-outline\"></ion-icon>\n </div>\n <div class=\"dcf-width-expand\">\n <ion-text color=\"danger\" class=\"dcf-text-small\">{{ locale + '.not_unique' | translate : { value: isUniqueError } }}</ion-text>\n </div>\n </div>\n </div>\n }\n <div class=\"dcf-margin-bottom dcf-grid dcf-grid-collapse dcf-flex\">\n @if(updatingItem) {\n <ion-button size=\"small\" fill=\"clear\" color=\"danger\" (click)=\"handleCancelUpdateItem()\">\n {{ buttonCancelLabel }}\n </ion-button>\n }\n <ion-button size=\"small\" fill=\"clear\" class=\"dcf-button-add\" (click)=\"handleCreateItem()\">\n <ion-icon name=\"add-outline\" slot=\"start\"></ion-icon>\n {{buttonLabel}}\n </ion-button>\n\n </div>\n }\n\n </div>\n </ion-accordion>\n </ion-accordion-group>\n</fieldset>\n\n", styles: ["ion-accordion-group ion-item[slot=header] .dcf-delete{width:30px}ion-accordion-group ion-item[slot=header] .dcf-delete ion-button{transform:translateY(-2px)}ion-accordion-group ion-item[slot=header] .dcf-delete ion-icon{font-size:1.15rem}::ng-deep ion-accordion ngx-decaf-crud-field:last-child ion-item{--inner-border-width: 0px !important;--border-width: 0px !important}.dcf-fieldset{margin-bottom:1.8rem;margin-top:1rem;padding-bottom:0;padding-top:1rem;background:var(--dcf-card-background);border-radius:6px;height:100%}@media (prefers-color-scheme: light){.dcf-fieldset{border:1px solid var(--dcf-color-gray-3)}.dcf-fieldset .dcf-button-add{color:var(--ion-color-dark)!important}}@media (prefers-color-scheme: dark){.dcf-fieldset{border:1px solid var(--dcf-color-step-400)}.dcf-fieldset .dcf-button-add{color:var(--ion-color-gray-2)}}.dcf-fieldset.read,.dcf-fieldset.delete{margin-top:1.25rem;padding-bottom:1rem}.dcf-fieldset.read [slot=content],.dcf-fieldset.delete [slot=content]{padding-top:0!important}.dcf-fieldset.read ion-accordion,.dcf-fieldset.delete ion-accordion{margin-bottom:0rem!important}@media (prefers-color-scheme: dark){.dcf-fieldset.read,.dcf-fieldset.delete{border:1px solid var(--dcf-color-gray-6)}}.dcf-fieldset ion-accordion{background:var(--dcf-card-background);margin-bottom:1rem}.dcf-fieldset ion-accordion.accordion-collapsing,.dcf-fieldset ion-accordion.accordion-collapsed{margin-bottom:1rem}.dcf-fieldset ion-accordion ion-item[slot=header]{--border-color: transparent;--border-radius: 6px;--inner-border-width: 0;--padding-start: 12px}.dcf-fieldset ion-accordion ion-item[slot=header] legend{font-weight:600;font-size:1rem;margin:0}@media (prefers-color-scheme: light){.dcf-fieldset ion-accordion ion-item[slot=header] legend{color:var(--dcf-color-gray-7)}}.dcf-fieldset ion-accordion [slot=content]{padding-top:2rem!important;padding-inline:.75rem}.dcf-not-unique-container{display:flex;justify-content:center;align-items:center;margin-bottom:1rem;flex:1 1 auto}.dcf-not-unique-container>div{display:flex;justify-content:center;align-items:center}.dcf-not-unique-container ion-icon{transform:translatey(2px);margin-right:5px}.dcf-fields-list{margin-top:-1rem;margin-bottom:1rem;padding:0!important}.dcf-fields-list ion-item{--min-height: 50px;--padding-top: .25rem;--padding-bottom: .25rem;--padding-start: .75rem;--padding-end: .75rem;--inner-padding-start: 0px !important;--inner-padding-end: 0px !important;--border-color: var(--dcf-color-gray-2) !important;border:1px solid transparent;box-sizing:border-box}.dcf-fields-list ion-item ion-icon.dcf-reorder-disabled{width:1rem;transform:translatey(2px);color:var(--dcf-color-gray-4)}.dcf-fields-list ion-item.updating{--background: rgba(var(--dcf-color-primary-rgb), .1) !important}.dcf-fields-list ion-item.not-unique{--background: rgba(var(--dcf-color-danger-rgb), .05) !important}.dcf-fields-list ion-item .dcf-subtitle{font-size:.8rem;color:var(--dcf-color-gray-7)}\n"] }]
4605
5027
  }], ctorParameters: () => [], propDecorators: { accordionComponent: [{
4606
5028
  type: ViewChild,
4607
5029
  args: ['accordionComponent', { static: false }]
@@ -4609,6 +5031,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4609
5031
  type: Input
4610
5032
  }], childOf: [{
4611
5033
  type: Input
5034
+ }], page: [{
5035
+ type: Input
4612
5036
  }], uid: [{
4613
5037
  type: Input
4614
5038
  }], customTypes: [{
@@ -5832,6 +6256,11 @@ class ModelRendererComponent {
5832
6256
  * @description Global variables to be passed to the rendered component
5833
6257
  */
5834
6258
  this.globals = {};
6259
+ /**
6260
+ * @description Set if render content projection is allowed
6261
+ * @default true
6262
+ */
6263
+ this.projectable = true;
5835
6264
  /**
5836
6265
  * @description Event emitter for custom events from the rendered component
5837
6266
  */
@@ -5849,7 +6278,7 @@ class ModelRendererComponent {
5849
6278
  typeof model === 'string'
5850
6279
  ? Model.build({}, model)
5851
6280
  : model;
5852
- this.output = model.render(this.globals || {}, this.vcr, this.injector, this.inner);
6281
+ this.output = model.render(this.globals || {}, this.vcr, this.injector, this.inner, this.projectable);
5853
6282
  if (this.output?.inputs)
5854
6283
  this.rendererId = sf(AngularEngineKeys.RENDERED_ID, this.output.inputs['rendererId']);
5855
6284
  this.instance = this.output?.instance;
@@ -5907,7 +6336,7 @@ class ModelRendererComponent {
5907
6336
  }
5908
6337
  }
5909
6338
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModelRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5910
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ModelRendererComponent, isStandalone: true, selector: "ngx-decaf-model-renderer", inputs: { model: "model", globals: "globals", rendererId: "rendererId" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "rendererId" } }, viewQueries: [{ propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }, { propertyName: "vcr", first: true, predicate: ["componentOuter"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: " <!-- Keep to avoid id conflicts -->\n <div [id]=\"rendererId\"></div>\n\n <ng-template #componentOuter></ng-template>\n <ng-template #inner>\n <div [id]=\"rendererId || null\">\n @for (child of output?.children; track child) {\n @if(child?.children?.length) {\n <ngx-decaf-component-renderer [parent]=\"child\" />\n } @else {\n <ng-container\n #childComponents\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n </div>\n </ng-template>\n", styles: [""], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "model", "parent"], outputs: ["listenEvent"] }] }); }
6339
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ModelRendererComponent, isStandalone: true, selector: "ngx-decaf-model-renderer", inputs: { model: "model", globals: "globals", projectable: "projectable", rendererId: "rendererId" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "rendererId" } }, viewQueries: [{ propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }, { propertyName: "vcr", first: true, predicate: ["componentOuter"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: " <!-- Keep to avoid id conflicts -->\n <div [id]=\"rendererId\"></div>\n\n <ng-template #componentOuter></ng-template>\n <ng-template #inner>\n <div [id]=\"rendererId || null\">\n @for (child of output?.children; track child) {\n @if(child?.children?.length) {\n <ngx-decaf-component-renderer [parent]=\"child\" />\n } @else {\n <ng-container\n #childComponents\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n </div>\n </ng-template>\n", styles: [""], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "model", "parent"], outputs: ["listenEvent"] }] }); }
5911
6340
  }
5912
6341
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModelRendererComponent, decorators: [{
5913
6342
  type: Component,
@@ -5917,6 +6346,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5917
6346
  args: [{ required: true }]
5918
6347
  }], globals: [{
5919
6348
  type: Input
6349
+ }], projectable: [{
6350
+ type: Input
5920
6351
  }], inner: [{
5921
6352
  type: ViewChild,
5922
6353
  args: ['inner', { read: TemplateRef, static: true }]
@@ -6050,40 +6481,15 @@ class LayoutComponent extends NgxBaseComponent {
6050
6481
  *
6051
6482
  * @memberOf LayoutComponent
6052
6483
  */
6053
- ngOnInit() {
6054
- this.initialize();
6055
- }
6056
- /**
6057
- * @description Initializes the layout component with processed properties.
6058
- * @summary Overrides the base component's initialize method to set up the grid layout.
6059
- * This method processes input properties, normalizes the breakpoint value, converts
6060
- * rows and columns to their array representations, and marks the component as initialized.
6061
- * The initialization ensures all properties are in the correct format for rendering.
6062
- *
6063
- * @mermaid
6064
- * sequenceDiagram
6065
- * participant L as LayoutComponent
6066
- * participant B as NgxBaseComponent
6067
- *
6068
- * L->>B: parseProps(this)
6069
- * Note over L: Process component properties
6070
- * L->>L: Normalize breakpoint to lowercase
6071
- * L->>L: Convert rows to array format
6072
- * L->>L: Convert cols to array format
6073
- * L->>L: Set initialized = true
6074
- *
6075
- * @override
6076
- * @memberOf LayoutComponent
6077
- */
6078
- initialize() {
6079
- this.parseProps(this);
6484
+ async ngOnInit() {
6485
+ await this.initialize();
6080
6486
  this.breakpoint = this.breakpoint.slice(0, 2).toLowerCase();
6081
6487
  this.cols = this._cols;
6082
6488
  this.rows = this._rows;
6083
6489
  this.initialized = true;
6084
6490
  }
6085
6491
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6086
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LayoutComponent, isStandalone: true, selector: "ngx-decaf-layout", inputs: { cols: "cols", rows: "rows", breakpoint: "breakpoint", children: "children" }, usesInheritance: true, ngImport: i0, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\" class=\"dcf-grid dcf-grid-collapse dcf-grid-match\">\n @if(row) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"(child.col === cols.length ? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+'@'+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: ModelRendererComponent, selector: "ngx-decaf-model-renderer", inputs: ["model", "globals", "rendererId"], outputs: ["listenEvent"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "model", "parent"], outputs: ["listenEvent"] }] }); }
6492
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LayoutComponent, isStandalone: true, selector: "ngx-decaf-layout", inputs: { cols: "cols", rows: "rows", breakpoint: "breakpoint", children: "children" }, usesInheritance: true, ngImport: i0, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\" class=\"dcf-grid dcf-grid-collapse dcf-grid-match\">\n @if(row) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"(child.col === cols.length ? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+'@'+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: ModelRendererComponent, selector: "ngx-decaf-model-renderer", inputs: ["model", "globals", "projectable", "rendererId"], outputs: ["listenEvent"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "model", "parent"], outputs: ["listenEvent"] }] }); }
6087
6493
  }
6088
6494
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LayoutComponent, decorators: [{
6089
6495
  type: Component,
@@ -7237,7 +7643,7 @@ let ListComponent = class ListComponent extends NgxBaseComponent {
7237
7643
  await this.refresh();
7238
7644
  if (this.operations.includes(OperationKeys.CREATE) && this.route)
7239
7645
  this.empty.link = `${this.route}/${OperationKeys.CREATE}`;
7240
- this.initialize();
7646
+ await this.initialize();
7241
7647
  if (this.model instanceof Model && this._repository)
7242
7648
  this._repository.observe(this.observer);
7243
7649
  }
@@ -7933,7 +8339,7 @@ let ListComponent = class ListComponent extends NgxBaseComponent {
7933
8339
  }, []);
7934
8340
  }
7935
8341
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7936
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ListComponent, isStandalone: true, selector: "ngx-decaf-list", inputs: { type: "type", translatable: "translatable", showSearchbar: "showSearchbar", data: "data", source: "source", start: "start", limit: "limit", loadMoreData: "loadMoreData", lines: "lines", inset: "inset", scrollThreshold: "scrollThreshold", scrollPosition: "scrollPosition", loadingText: "loadingText", showRefresher: "showRefresher", loadingSpinner: "loadingSpinner", enableFilter: "enableFilter", sortDirection: "sortDirection", sortBy: "sortBy", disableSort: "disableSort", emptyIcon: "emptyIcon", empty: "empty" }, outputs: { refreshEvent: "refreshEvent", clickEvent: "clickEvent" }, host: { listeners: { "window:ListItemClickEvent": "handleClick($event)", "window:searchbarEvent": "handleSearch($event)", "window:BackButtonNavigationEndEvent": "refresh($event)" } }, usesInheritance: true, ngImport: i0, template: "\n@if(showRefresher) {\n <ion-refresher slot=\"fixed\" [pullFactor]=\"1\" [pullMin]=\"100\" [pullMax]=\"200\" (ionRefresh)=\"handleRefresh($event)\">\n <ion-refresher-content />\n </ion-refresher>\n}\n\n@if(showSearchbar && data?.length) {\n @if(model && enableFilter) {\n <ngx-decaf-filter\n [model]=\"model\"\n [sortDirection]=\"sortDirection\"\n [disableSort]=\"disableSort\"\n (filterEvent)=\"handleFilter($event)\"\n (searchEvent)=\"handleSearch($event)\"\n />\n } @else {\n <ngx-decaf-searchbar [emitEventToWindow]=\"false\" [debounce]=\"500\" (searchEvent)=\"handleSearch($event)\" />\n }\n}\n\n@if(data?.length) {\n <ion-list [id]=\"uid\" [inset]=\"inset\" [lines]=\"lines\" #component>\n @if(item?.tag) {\n @for(child of items; track trackItemFn($index, child)) {\n <ngx-decaf-component-renderer\n [tag]=\"item.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]='{\n item: child,\n mapper: mapper,\n route: route\n }'>\n </ngx-decaf-component-renderer>\n }\n } @else {\n <ng-content></ng-content>\n }\n </ion-list>\n\n @if(loadMoreData) {\n @if(pages > 0 && type === 'paginated' && !searchValue?.length) {\n <ngx-decaf-pagination\n [totalPages]=\"pages\"\n [current]=\"page\"\n (clickEvent)=\"handlePaginate($event)\"\n />\n\n } @else {\n <ion-infinite-scroll\n [class]=\"searchValue?.length ? 'dcf-hidden' : ''\"\n\n [position]=\"scrollPosition\"\n [threshold]=\"scrollThreshold\"\n (ionInfinite)=\"handleRefresh($event)\">\n <ion-infinite-scroll-content [loadingSpinner]=\"loadingSpinner\" [loadingText]=\"loadingText\" />\n </ion-infinite-scroll>\n }\n }\n} @else {\n @if(refreshing) {\n @for(skl of skeletonData; track $index) {\n <ion-item>\n <ion-thumbnail slot=\"start\">\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n </ion-thumbnail>\n <ion-label>\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n <ion-text class=\"date\" style=\"width: 20%;\"><ion-skeleton-text [animated]=\"true\"></ion-skeleton-text></ion-text>\n </ion-label>\n </ion-item>\n }\n\n } @else {\n @if(!searchValue?.length) {\n <ngx-decaf-empty-state\n [title]=\"(locale + '.'+ empty.title) | translate\"\n [subtitle]=\"(locale + '.'+ empty.subtitle) | translate\"\n [buttonText]=\"empty.showButton ? (locale + '.'+ empty.button | translate) : ''\"\n [buttonLink]=\"empty.showButton ? empty.route : ''\"\n />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n ngClass=\"empty-search\"\n [translatable]=\"true\"\n title=\"search.title\"\n subtitle=\"search.subtitle\"\n [searchValue]=\"searchValue\"\n />\n }\n }\n}\n\n", styles: ["ion-infinite-scroll{max-height:50px}ion-infinite-scroll:not(.infinite-scroll-loading) ::ng-deep{max-height:1.5rem}ion-infinite-scroll ::ng-deep ion-spinner{--color: var(--dcf-color-primary);padding-top:1rem}@media (max-width: 768px){#end,[slot=end]{display:none!important}}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: PaginationComponent, selector: "ngx-decaf-pagination", inputs: ["totalPages", "current"], outputs: ["clickEvent"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonThumbnail, selector: "ion-thumbnail" }, { kind: "component", type: IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: SearchbarComponent, selector: "ngx-decaf-searchbar", inputs: ["autocomplete", "autocorrect", "animated", "buttonCancelText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value", "queryKeys", "isVisible", "wrapper", "wrapperColor", "emitEventToWindow"], outputs: ["searchEvent"] }, { kind: "component", type: EmptyStateComponent, selector: "ngx-decaf-empty-state", inputs: ["title", "titleColor", "subtitle", "subtitleColor", "showIcon", "icon", "iconSize", "iconColor", "buttonLink", "buttonText", "buttonFill", "buttonColor", "buttonSize", "searchValue"] }, { kind: "component", type: FilterComponent, selector: "ngx-decaf-filter", inputs: ["indexes", "conditions", "sortBy", "disableSort"], outputs: ["filterEvent", "searchEvent"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "model", "parent"], outputs: ["listenEvent"] }] }); }
8342
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ListComponent, isStandalone: true, selector: "ngx-decaf-list", inputs: { type: "type", translatable: "translatable", showSearchbar: "showSearchbar", data: "data", source: "source", start: "start", limit: "limit", loadMoreData: "loadMoreData", lines: "lines", inset: "inset", scrollThreshold: "scrollThreshold", scrollPosition: "scrollPosition", loadingText: "loadingText", showRefresher: "showRefresher", loadingSpinner: "loadingSpinner", enableFilter: "enableFilter", sortDirection: "sortDirection", sortBy: "sortBy", disableSort: "disableSort", emptyIcon: "emptyIcon", empty: "empty" }, outputs: { refreshEvent: "refreshEvent", clickEvent: "clickEvent" }, host: { listeners: { "window:ListItemClickEvent": "handleClick($event)", "window:searchbarEvent": "handleSearch($event)", "window:BackButtonNavigationEndEvent": "refresh($event)" } }, usesInheritance: true, ngImport: i0, template: "\n@if(showRefresher) {\n <ion-refresher slot=\"fixed\" [pullFactor]=\"1\" [pullMin]=\"100\" [pullMax]=\"200\" (ionRefresh)=\"handleRefresh($event)\">\n <ion-refresher-content />\n </ion-refresher>\n}\n\n@if(showSearchbar && data?.length) {\n @if(model && enableFilter) {\n <ngx-decaf-filter\n [model]=\"model\"\n [sortDirection]=\"sortDirection\"\n [disableSort]=\"disableSort\"\n (filterEvent)=\"handleFilter($event)\"\n (searchEvent)=\"handleSearch($event)\"\n />\n } @else {\n <ngx-decaf-searchbar [emitEventToWindow]=\"false\" [debounce]=\"500\" (searchEvent)=\"handleSearch($event)\" />\n }\n}\n\n@if(data?.length) {\n <ion-list [id]=\"uid\" [inset]=\"inset\" [lines]=\"lines\" #component>\n @if(item?.tag) {\n @for(child of items; track trackItemFn($index, child)) {\n <ngx-decaf-component-renderer\n [tag]=\"item.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]='{\n item: child,\n mapper: mapper,\n route: route\n }'>\n </ngx-decaf-component-renderer>\n }\n } @else {\n <ng-content></ng-content>\n }\n </ion-list>\n\n @if(loadMoreData) {\n @if(pages > 0 && type === 'paginated' && !searchValue?.length) {\n <ngx-decaf-pagination\n [totalPages]=\"pages\"\n [current]=\"page\"\n (clickEvent)=\"handlePaginate($event)\"\n />\n\n } @else {\n <ion-infinite-scroll\n [class]=\"searchValue?.length ? 'dcf-hidden' : ''\"\n\n [position]=\"scrollPosition\"\n [threshold]=\"scrollThreshold\"\n (ionInfinite)=\"handleRefresh($event)\">\n <ion-infinite-scroll-content [loadingSpinner]=\"loadingSpinner\" [loadingText]=\"loadingText\" />\n </ion-infinite-scroll>\n }\n }\n} @else {\n @if(refreshing) {\n @for(skl of skeletonData; track $index) {\n <ion-item>\n <ion-thumbnail slot=\"start\">\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n </ion-thumbnail>\n <ion-label>\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n <ion-text class=\"date\" style=\"width: 20%;\"><ion-skeleton-text [animated]=\"true\"></ion-skeleton-text></ion-text>\n </ion-label>\n </ion-item>\n }\n\n } @else {\n @if(!searchValue?.length) {\n <ngx-decaf-empty-state\n [title]=\"(locale + '.'+ empty.title) | translate\"\n [subtitle]=\"(locale + '.'+ empty.subtitle) | translate\"\n [buttonText]=\"empty.showButton ? (locale + '.'+ empty.button | translate) : ''\"\n [buttonLink]=\"empty.showButton ? empty.route : ''\"\n />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n ngClass=\"empty-search\"\n [translatable]=\"true\"\n title=\"search.title\"\n subtitle=\"search.subtitle\"\n [searchValue]=\"searchValue\"\n />\n }\n }\n}\n\n", styles: ["ion-infinite-scroll{max-height:50px}ion-infinite-scroll:not(.infinite-scroll-loading) ::ng-deep{max-height:1.5rem}ion-infinite-scroll ::ng-deep ion-spinner{--color: var(--dcf-color-primary);padding-top:1rem}@media (max-width: 768px){#end,[slot=end]{display:none!important}}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: PaginationComponent, selector: "ngx-decaf-pagination", inputs: ["totalPages", "current"], outputs: ["clickEvent"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonThumbnail, selector: "ion-thumbnail" }, { kind: "component", type: IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: SearchbarComponent, selector: "ngx-decaf-searchbar", inputs: ["autocomplete", "autocorrect", "animated", "buttonCancelText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value", "queryKeys", "isVisible", "wrapper", "wrapperColor", "emitEventToWindow"], outputs: ["searchEvent"] }, { kind: "component", type: EmptyStateComponent, selector: "ngx-decaf-empty-state", inputs: ["title", "titleColor", "subtitle", "subtitleColor", "showIcon", "icon", "iconSize", "iconColor", "buttonLink", "buttonText", "buttonFill", "buttonColor", "buttonSize", "searchValue"] }, { kind: "component", type: FilterComponent, selector: "ngx-decaf-filter", inputs: ["indexes", "conditions", "sortBy", "disableSort"], outputs: ["filterEvent", "searchEvent"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "model", "parent"], outputs: ["listenEvent"] }] }); }
7937
8343
  };
7938
8344
  ListComponent = __decorate([
7939
8345
  Dynamic(),
@@ -8020,6 +8426,296 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
8020
8426
  args: ['window:BackButtonNavigationEndEvent', ['$event']]
8021
8427
  }] } });
8022
8428
 
8429
+ let SteppedFormComponent = class SteppedFormComponent {
8430
+ /**
8431
+ * @description Creates an instance of SteppedFormComponent.
8432
+ * @summary Initializes a new SteppedFormComponent instance and registers the required
8433
+ * Ionic icons for navigation buttons (forward and back arrows).
8434
+ *
8435
+ * @memberOf SteppedFormComponent
8436
+ */
8437
+ constructor() {
8438
+ /**
8439
+ * @description Number of pages in the stepped form.
8440
+ * @summary Represents the total number of steps/pages in the multi-step form.
8441
+ * This value is automatically calculated based on the page properties of the children
8442
+ * or can be explicitly set. Each page represents a logical group of form fields.
8443
+ *
8444
+ * @type {number}
8445
+ * @default 1
8446
+ * @memberOf SteppedFormComponent
8447
+ */
8448
+ this.pages = 1;
8449
+ /**
8450
+ * @description The CRUD operation type for this form.
8451
+ * @summary Defines the type of operation being performed (CREATE, READ, UPDATE, DELETE).
8452
+ * This affects form behavior, validation rules, and field accessibility. For example,
8453
+ * READ operations might disable form fields, while CREATE operations enable all fields.
8454
+ *
8455
+ * @type {CrudOperations}
8456
+ * @default OperationKeys.CREATE
8457
+ * @memberOf SteppedFormComponent
8458
+ */
8459
+ this.operation = OperationKeys.CREATE;
8460
+ /**
8461
+ * @description The initial page to display when the form loads.
8462
+ * @summary Specifies which page of the multi-step form should be shown first.
8463
+ * This allows starting the form at any step, useful for scenarios like editing
8464
+ * existing data where you might want to jump to a specific section.
8465
+ *
8466
+ * @type {number}
8467
+ * @default 1
8468
+ * @memberOf SteppedFormComponent
8469
+ */
8470
+ this.startPage = 1;
8471
+ /**
8472
+ * @description Array of UI model metadata for the currently active page.
8473
+ * @summary Contains only the UI model metadata for fields that should be displayed
8474
+ * on the currently active page. This is a filtered subset of the children array,
8475
+ * updated whenever the user navigates between pages.
8476
+ *
8477
+ * @type {UIModelMetadata[] | undefined}
8478
+ * @memberOf SteppedFormComponent
8479
+ */
8480
+ this.activeChildren = undefined;
8481
+ /**
8482
+ * @description FormGroup for the currently active page.
8483
+ * @summary The FormGroup instance that manages form controls and validation
8484
+ * for the current page only. This is extracted from the main formGroup
8485
+ * when using FormArray structure.
8486
+ *
8487
+ * @type {FormGroup | undefined}
8488
+ * @memberOf SteppedFormComponent
8489
+ */
8490
+ this.activeFormGroup = undefined;
8491
+ /**
8492
+ * @description The currently active page number.
8493
+ * @summary Tracks which page of the multi-step form is currently being displayed.
8494
+ * This property is updated as users navigate through the form steps using
8495
+ * the next/back buttons or programmatic navigation.
8496
+ *
8497
+ * @type {number}
8498
+ * @memberOf SteppedFormComponent
8499
+ */
8500
+ this.activePage = 1;
8501
+ /**
8502
+ * @description Array representing the structure of pages.
8503
+ * @summary Contains metadata about each page, including page numbers and indices.
8504
+ * This array is built during initialization to organize the form fields into
8505
+ * logical pages and provide navigation structure.
8506
+ *
8507
+ * @type {UIModelMetadata[]}
8508
+ * @memberOf SteppedFormComponent
8509
+ */
8510
+ this.pagesArray = [];
8511
+ /**
8512
+ * @description Event emitter for form submission.
8513
+ * @summary Emits events when the form is submitted, typically on the last page
8514
+ * when all validation passes. The emitted event contains the form data and
8515
+ * event type information for parent components to handle.
8516
+ *
8517
+ * @type {EventEmitter<BaseCustomEvent>}
8518
+ * @memberOf SteppedFormComponent
8519
+ */
8520
+ this.submitEvent = new EventEmitter();
8521
+ addIcons({ arrowForwardOutline, arrowBackOutline });
8522
+ }
8523
+ /**
8524
+ * @description Initializes the component after Angular first displays the data-bound properties.
8525
+ * @summary Sets up the stepped form by organizing children into pages, calculating the total
8526
+ * number of pages, and initializing the active page. This method processes the UI model metadata
8527
+ * to create a logical page structure and ensures proper page assignments for all form fields.
8528
+ *
8529
+ * @mermaid
8530
+ * sequenceDiagram
8531
+ * participant A as Angular Lifecycle
8532
+ * participant S as SteppedFormComponent
8533
+ * participant F as Form Service
8534
+ *
8535
+ * A->>S: ngOnInit()
8536
+ * S->>S: Set activePage = startPage
8537
+ * S->>S: Process children into pagesArray
8538
+ * S->>S: Calculate total pages
8539
+ * S->>S: Assign page props to children
8540
+ * S->>S: getCurrentFormGroup(activePage)
8541
+ * S->>F: Extract FormGroup for active page
8542
+ * F-->>S: Return activeFormGroup
8543
+ *
8544
+ * @memberOf SteppedFormComponent
8545
+ */
8546
+ ngOnInit() {
8547
+ if (!this.locale)
8548
+ this.locale = getLocaleContext("SteppedFormComponent");
8549
+ this.activePage = this.startPage;
8550
+ this.pagesArray = this.children.reduce((acc, curr, index) => {
8551
+ const page = curr.props?.['page'] || index + 1;
8552
+ if (!acc[page])
8553
+ acc[page] = [];
8554
+ acc[page].push({ index: page });
8555
+ return acc;
8556
+ }, []).filter(Boolean);
8557
+ this.pages = this.pagesArray.length;
8558
+ this.children = [...this.children.map((c) => {
8559
+ if (!c.props)
8560
+ c.props = {};
8561
+ const page = c.props['page'] || 1;
8562
+ // prevent page overflow
8563
+ c.props['page'] = page > this.pages ? this.pages : page;
8564
+ return c;
8565
+ })];
8566
+ this.getCurrentFormGroup(this.activePage);
8567
+ }
8568
+ /**
8569
+ * @description Cleanup method called when the component is destroyed.
8570
+ * @summary Unsubscribes from any active timer subscriptions to prevent memory leaks.
8571
+ * This is part of Angular's component lifecycle and ensures proper resource cleanup.
8572
+ *
8573
+ * @memberOf SteppedFormComponent
8574
+ */
8575
+ ngOnDestroy() {
8576
+ if (this.timerSubscription)
8577
+ this.timerSubscription.unsubscribe();
8578
+ }
8579
+ /**
8580
+ * @description Handles navigation to the next page or form submission.
8581
+ * @summary Validates the current page's form fields and either navigates to the next page
8582
+ * or submits the entire form if on the last page. Form validation must pass before
8583
+ * proceeding. On successful submission, emits a submit event with form data.
8584
+ *
8585
+ * @param {boolean} lastPage - Whether this is the last page of the form
8586
+ * @return {void}
8587
+ *
8588
+ * @mermaid
8589
+ * sequenceDiagram
8590
+ * participant U as User
8591
+ * participant S as SteppedFormComponent
8592
+ * participant F as Form Service
8593
+ * participant P as Parent Component
8594
+ *
8595
+ * U->>S: Click Next/Submit
8596
+ * S->>F: validateFields(activeFormGroup)
8597
+ * F-->>S: Return validation result
8598
+ * alt Not last page and valid
8599
+ * S->>S: activePage++
8600
+ * S->>S: getCurrentFormGroup(activePage)
8601
+ * else Last page and valid
8602
+ * S->>F: getFormData(formGroup)
8603
+ * F-->>S: Return form data
8604
+ * S->>P: submitEvent.emit({data, name: SUBMIT})
8605
+ * end
8606
+ *
8607
+ * @memberOf SteppedFormComponent
8608
+ */
8609
+ handleNext(lastPage = false) {
8610
+ const isValid = NgxFormService.validateFields(this.activeFormGroup);
8611
+ if (!lastPage) {
8612
+ if (isValid) {
8613
+ this.activePage = this.activePage + 1;
8614
+ this.getCurrentFormGroup(this.activePage);
8615
+ }
8616
+ }
8617
+ else {
8618
+ if (isValid) {
8619
+ const data = Object.assign({}, ...Object.values(NgxFormService.getFormData(this.formGroup)));
8620
+ this.submitEvent.emit({
8621
+ data,
8622
+ name: EventConstants.SUBMIT,
8623
+ });
8624
+ }
8625
+ }
8626
+ }
8627
+ /**
8628
+ * @description Handles navigation to the previous page.
8629
+ * @summary Moves the user back to the previous page in the stepped form.
8630
+ * This method decrements the active page number and updates the form
8631
+ * group and children to display the previous page's content.
8632
+ *
8633
+ * @return {void}
8634
+ *
8635
+ * @mermaid
8636
+ * sequenceDiagram
8637
+ * participant U as User
8638
+ * participant S as SteppedFormComponent
8639
+ *
8640
+ * U->>S: Click Back
8641
+ * S->>S: activePage--
8642
+ * S->>S: getCurrentFormGroup(activePage)
8643
+ * Note over S: Update active form and children
8644
+ *
8645
+ * @memberOf SteppedFormComponent
8646
+ */
8647
+ handleBack() {
8648
+ this.activePage = this.activePage - 1;
8649
+ this.getCurrentFormGroup(this.activePage);
8650
+ }
8651
+ /**
8652
+ * @description Updates the active form group and children for the specified page.
8653
+ * @summary Extracts the FormGroup for the given page from the FormArray and filters
8654
+ * the children to show only fields belonging to that page. Uses a timer to ensure
8655
+ * proper Angular change detection when updating the activeChildren.
8656
+ *
8657
+ * @param {number} page - The page number to activate
8658
+ * @return {void}
8659
+ *
8660
+ * @private
8661
+ * @mermaid
8662
+ * sequenceDiagram
8663
+ * participant S as SteppedFormComponent
8664
+ * participant F as FormArray
8665
+ * participant T as Timer
8666
+ *
8667
+ * S->>F: Extract FormGroup at index (page - 1)
8668
+ * F-->>S: Return page FormGroup
8669
+ * S->>S: Set activeChildren = undefined
8670
+ * S->>T: timer(10).subscribe()
8671
+ * T-->>S: Filter children for active page
8672
+ * S->>S: Set activeChildren
8673
+ *
8674
+ * @memberOf SteppedFormComponent
8675
+ */
8676
+ getCurrentFormGroup(page) {
8677
+ this.activeFormGroup = this.formGroup.at(page - 1);
8678
+ this.activeChildren = undefined;
8679
+ this.timerSubscription = timer(10).subscribe(() => {
8680
+ this.activeChildren = this.children.filter(c => c.props?.['page'] === page);
8681
+ console.log(this.activeChildren);
8682
+ });
8683
+ }
8684
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SteppedFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8685
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SteppedFormComponent, isStandalone: true, selector: "ngx-decaf-stepped-form", inputs: { locale: "locale", pages: "pages", operation: "operation", startPage: "startPage", children: "children", formGroup: "formGroup" }, outputs: { submitEvent: "submitEvent" }, ngImport: i0, template: "<form class=\"dcf-steped-form\" novalidate>\n <div class=\"dcf-page-steps\">\n <div>\n @for(page of pagesArray; track $index;) {\n <div [class.dcf-active]=\"activePage === $index + 1\" [class.dcf-passed]=\"($index + 1) < activePage\">{{ $index + 1 }}</div>\n }\n </div>\n </div>\n @if(formGroup) {\n @for(child of activeChildren; track $index) {\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n }\n } @else {\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n <ion-text class=\"date\" style=\"width: 20%;\"><ion-skeleton-text [animated]=\"true\"></ion-skeleton-text></ion-text>\n <br />\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n <ion-text class=\"date\" style=\"width: 20%;\"><ion-skeleton-text [animated]=\"true\"></ion-skeleton-text></ion-text>\n }\n\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left\">\n <div class=\"dcf-width-1-2@s\">\n <ion-button fill=\"clear\" (click)=\"handleBack()\" [disabled]=\"activePage <= 1\">\n <ion-icon aria-hidden=\"true\" name=\"arrow-back-outline\"></ion-icon>\n {{locale + '.previous' | translate}}\n </ion-button>\n </div>\n\n <div class=\"dcf-width-1-2@s\">\n <ion-button [fill]=\"activePage === pages ? 'solid' : 'outline'\" (click)=\"handleNext(activePage === pages ? true : false)\">\n @if(activePage === pages) {\n {{locale + '.submit' | translate}}\n } @else {\n {{locale + '.next' | translate}}\n <ion-icon aria-hidden=\"true\" name=\"arrow-forward-outline\"></ion-icon>\n }\n </ion-button>\n </div>\n </div>\n</form>\n", styles: [".dcf-buttons-container{margin-top:1.8rem;margin-bottom:0}@media (min-width: 639px){.dcf-buttons-container.dcf-flex div:nth-child(2){display:flex;justify-content:flex-end}}@media (max-width: 638px){.dcf-buttons-container.dcf-flex div{width:100%}.dcf-buttons-container.dcf-flex ion-button{width:100%;margin-bottom:1rem}}.dcf-steped-form{padding:2rem 1rem}.dcf-page-steps{display:flex;justify-content:center;margin-bottom:2rem}.dcf-page-steps>div{justify-content:center;min-width:200px;max-width:200px;column-gap:.25em;display:flex}.dcf-page-steps>div>div{width:30px;text-align:center;border-bottom:solid var(--dcf-color-gray-3);box-sizing:border-box;border-width:3px;font-size:0px;font-weight:600}.dcf-page-steps>div>div.dcf-active{border-width:5px;font-size:1rem;color:var(--ion-color-gray-7);border-color:var(--ion-color-primary);line-height:1rem}.dcf-page-steps>div>div.dcf-passed{border-width:4px;font-size:.5rem;line-height:1.2rem;border-color:var(--dcf-color-gray-5);color:var(--ion-color-primary)}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "model", "parent"], outputs: ["listenEvent"] }] }); }
8686
+ };
8687
+ SteppedFormComponent = __decorate([
8688
+ Dynamic(),
8689
+ __metadata("design:paramtypes", [])
8690
+ ], SteppedFormComponent);
8691
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SteppedFormComponent, decorators: [{
8692
+ type: Component,
8693
+ args: [{ selector: 'ngx-decaf-stepped-form', imports: [
8694
+ TranslatePipe,
8695
+ ReactiveFormsModule,
8696
+ IonSkeletonText,
8697
+ IonText,
8698
+ IonButton,
8699
+ IonIcon,
8700
+ ModelRendererComponent,
8701
+ ComponentRendererComponent
8702
+ ], standalone: true, template: "<form class=\"dcf-steped-form\" novalidate>\n <div class=\"dcf-page-steps\">\n <div>\n @for(page of pagesArray; track $index;) {\n <div [class.dcf-active]=\"activePage === $index + 1\" [class.dcf-passed]=\"($index + 1) < activePage\">{{ $index + 1 }}</div>\n }\n </div>\n </div>\n @if(formGroup) {\n @for(child of activeChildren; track $index) {\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n }\n } @else {\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n <ion-text class=\"date\" style=\"width: 20%;\"><ion-skeleton-text [animated]=\"true\"></ion-skeleton-text></ion-text>\n <br />\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n <ion-text class=\"date\" style=\"width: 20%;\"><ion-skeleton-text [animated]=\"true\"></ion-skeleton-text></ion-text>\n }\n\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-collapse dcf-flex dcf-flex-left\">\n <div class=\"dcf-width-1-2@s\">\n <ion-button fill=\"clear\" (click)=\"handleBack()\" [disabled]=\"activePage <= 1\">\n <ion-icon aria-hidden=\"true\" name=\"arrow-back-outline\"></ion-icon>\n {{locale + '.previous' | translate}}\n </ion-button>\n </div>\n\n <div class=\"dcf-width-1-2@s\">\n <ion-button [fill]=\"activePage === pages ? 'solid' : 'outline'\" (click)=\"handleNext(activePage === pages ? true : false)\">\n @if(activePage === pages) {\n {{locale + '.submit' | translate}}\n } @else {\n {{locale + '.next' | translate}}\n <ion-icon aria-hidden=\"true\" name=\"arrow-forward-outline\"></ion-icon>\n }\n </ion-button>\n </div>\n </div>\n</form>\n", styles: [".dcf-buttons-container{margin-top:1.8rem;margin-bottom:0}@media (min-width: 639px){.dcf-buttons-container.dcf-flex div:nth-child(2){display:flex;justify-content:flex-end}}@media (max-width: 638px){.dcf-buttons-container.dcf-flex div{width:100%}.dcf-buttons-container.dcf-flex ion-button{width:100%;margin-bottom:1rem}}.dcf-steped-form{padding:2rem 1rem}.dcf-page-steps{display:flex;justify-content:center;margin-bottom:2rem}.dcf-page-steps>div{justify-content:center;min-width:200px;max-width:200px;column-gap:.25em;display:flex}.dcf-page-steps>div>div{width:30px;text-align:center;border-bottom:solid var(--dcf-color-gray-3);box-sizing:border-box;border-width:3px;font-size:0px;font-weight:600}.dcf-page-steps>div>div.dcf-active{border-width:5px;font-size:1rem;color:var(--ion-color-gray-7);border-color:var(--ion-color-primary);line-height:1rem}.dcf-page-steps>div>div.dcf-passed{border-width:4px;font-size:.5rem;line-height:1.2rem;border-color:var(--dcf-color-gray-5);color:var(--ion-color-primary)}\n"] }]
8703
+ }], ctorParameters: () => [], propDecorators: { locale: [{
8704
+ type: Input
8705
+ }], pages: [{
8706
+ type: Input
8707
+ }], operation: [{
8708
+ type: Input
8709
+ }], startPage: [{
8710
+ type: Input
8711
+ }], children: [{
8712
+ type: Input
8713
+ }], formGroup: [{
8714
+ type: Input
8715
+ }], submitEvent: [{
8716
+ type: Output
8717
+ }] } });
8718
+
8023
8719
  class CollapsableDirective {
8024
8720
  constructor() {
8025
8721
  this.element = inject(ElementRef);
@@ -8061,7 +8757,8 @@ const Components = [
8061
8757
  CrudFormComponent,
8062
8758
  FieldsetComponent,
8063
8759
  LayoutComponent,
8064
- FilterComponent
8760
+ FilterComponent,
8761
+ SteppedFormComponent
8065
8762
  ];
8066
8763
  class ForAngularComponentsModule {
8067
8764
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ForAngularComponentsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
@@ -8077,7 +8774,8 @@ class ForAngularComponentsModule {
8077
8774
  CrudFormComponent,
8078
8775
  FieldsetComponent,
8079
8776
  LayoutComponent,
8080
- FilterComponent, CollapsableDirective], exports: [ModelRendererComponent,
8777
+ FilterComponent,
8778
+ SteppedFormComponent, CollapsableDirective], exports: [ModelRendererComponent,
8081
8779
  ComponentRendererComponent,
8082
8780
  CrudFieldComponent,
8083
8781
  CrudFormComponent,
@@ -8089,7 +8787,8 @@ class ForAngularComponentsModule {
8089
8787
  CrudFormComponent,
8090
8788
  FieldsetComponent,
8091
8789
  LayoutComponent,
8092
- FilterComponent, CollapsableDirective] }); }
8790
+ FilterComponent,
8791
+ SteppedFormComponent, CollapsableDirective] }); }
8093
8792
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ForAngularComponentsModule, imports: [CrudFieldComponent,
8094
8793
  CrudFormComponent,
8095
8794
  EmptyStateComponent,
@@ -8099,14 +8798,15 @@ class ForAngularComponentsModule {
8099
8798
  PaginationComponent,
8100
8799
  CrudFormComponent,
8101
8800
  FieldsetComponent,
8102
- FilterComponent] }); }
8801
+ FilterComponent,
8802
+ SteppedFormComponent] }); }
8103
8803
  }
8104
8804
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ForAngularComponentsModule, decorators: [{
8105
8805
  type: NgModule,
8106
8806
  args: [{
8107
8807
  imports: [...Components, ...Directives],
8108
8808
  declarations: [],
8109
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
8809
+ schemas: [],
8110
8810
  exports: [...Components, ...Directives],
8111
8811
  }]
8112
8812
  }] });
@@ -8125,5 +8825,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
8125
8825
  * Generated bundle index. Do not edit.
8126
8826
  */
8127
8827
 
8128
- export { AngularEngineKeys, BaseComponentProps, CollapsableDirective, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_PROVIDER_TOKEN, DefaultFormReactiveOptions, Dynamic, DynamicModule, EmptyStateComponent, EventConstants, FieldsetComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, FormConstants, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, LoggerLevels, ModelRendererComponent, MultiI18nLoader, NgxBaseComponent, NgxCrudFormField, NgxFormService, NgxRenderingEngine, PaginationComponent, RouteDirections, SearchbarComponent, cleanSpaces, dataMapper, formatDate, generateRandomValue, getI18nLoaderFactoryProviderConfig, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, parseToValidDate, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
8828
+ export { AngularEngineKeys, BaseComponentProps, CollapsableDirective, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_PROVIDER_TOKEN, DefaultFormReactiveOptions, Dynamic, DynamicModule, EmptyStateComponent, EventConstants, FieldsetComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, FormConstants, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, LoggerLevels, ModelRendererComponent, MultiI18nLoader, NgxBaseComponent, NgxCrudFormField, NgxFormService, NgxRenderingEngine, PaginationComponent, RouteDirections, SearchbarComponent, SteppedFormComponent, cleanSpaces, dataMapper, formatDate, generateRandomValue, getI18nLoaderFactoryProviderConfig, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, parseToValidDate, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
8129
8829
  //# sourceMappingURL=decaf-ts-for-angular.mjs.map