@decaf-ts/for-angular 0.0.36 → 0.0.37

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.
@@ -1,7 +1,7 @@
1
1
  import { UIKeys, HTML5InputTypes, parseValueByType, HTML5CheckTypes, escapeHtml, parseToNumber, RenderingEngine, RenderingError, UIMediaBreakPoints } from '@decaf-ts/ui-decorators';
2
2
  import * as i0 from '@angular/core';
3
3
  import { InjectionToken, NgModule, isDevMode, reflectComponentType, inject, ChangeDetectorRef, Renderer2, EventEmitter, ElementRef, Input, Output, ViewChild, Inject, Directive, EnvironmentInjector, ViewContainerRef, TemplateRef, ViewEncapsulation, Component, HostListener, CUSTOM_ELEMENTS_SCHEMA, Injector } from '@angular/core';
4
- import { CommonModule, NgComponentOutlet, Location } from '@angular/common';
4
+ import { CommonModule, Location, NgComponentOutlet } from '@angular/common';
5
5
  import { VALIDATION_PARENT_KEY, ValidationKeys, DEFAULT_PATTERNS, Validation, Primitives, ComparisonValidationKeys, PathProxyEngine, Model, ModelKeys, isValidDate as isValidDate$1, parseDate, sf, ReservedModels } from '@decaf-ts/decorator-validation';
6
6
  import { InternalError, OperationKeys } from '@decaf-ts/db-decorators';
7
7
  import * as i1 from '@angular/forms';
@@ -11,9 +11,9 @@ import { TranslateModule, TranslatePipe, TranslateParser, provideTranslateServic
11
11
  import { Logging, LoggedClass } from '@decaf-ts/logging';
12
12
  import { uses, Repository, OrderDirection, Condition } from '@decaf-ts/core';
13
13
  import { apply, metadata } from '@decaf-ts/reflection';
14
- import { Router } from '@angular/router';
15
- import { provideHttpClient, HttpClient } from '@angular/common/http';
14
+ import { Router, NavigationEnd, NavigationStart } from '@angular/router';
16
15
  import { forkJoin, firstValueFrom, fromEvent, debounceTime, Subject, timer } from 'rxjs';
16
+ import { provideHttpClient, HttpClient } from '@angular/common/http';
17
17
  import { map } from 'rxjs/operators';
18
18
  import { MenuController, NavController } from '@ionic/angular';
19
19
  import { Title, DomSanitizer } from '@angular/platform-browser';
@@ -24,9 +24,11 @@ import * as allIcons from 'ionicons/icons';
24
24
  import { chevronUpOutline, chevronDownOutline, createOutline, alertCircleOutline, chevronForwardOutline, chevronBackOutline, arrowBackOutline, arrowForwardOutline } from 'ionicons/icons';
25
25
 
26
26
  /**
27
- * @description Angular engine key constants
27
+ * @description Angular engine key constants.
28
28
  * @summary Contains key strings used by the Angular rendering engine for reflection,
29
- * dynamic component creation, and other engine operations.
29
+ * dynamic component creation, and other engine operations. These constants provide
30
+ * consistent naming for metadata keys, DOM attributes, and component identification
31
+ * throughout the rendering system.
30
32
  * @typedef {Object} AngularEngineKeys
31
33
  * @property {string} REFLECT - Prefix for reflection metadata keys
32
34
  * @property {string} DYNAMIC - Key for dynamic component identification
@@ -40,8 +42,10 @@ import { chevronUpOutline, chevronDownOutline, createOutline, alertCircleOutline
40
42
  * @property {string} RENDER - Key for renderable components
41
43
  * @property {string} RENDERED_ID - Template for rendered component IDs
42
44
  * @property {string} PARENT - Key for comparison decorators and validators
45
+ * @property {string} VALIDATION_PARENT_KEY - Key for validation parent reference
46
+ * @property {string} FLAVOUR - Identifier for the Angular engine flavor
43
47
  * @const AngularEngineKeys
44
- * @memberOf module:engine
48
+ * @memberOf module:lib/engine/constants
45
49
  */
46
50
  const AngularEngineKeys = {
47
51
  REFLECT: `${UIKeys.REFLECT}.angular.`,
@@ -57,32 +61,40 @@ const AngularEngineKeys = {
57
61
  RENDERED_ID: 'rendered-as-{0}',
58
62
  PARENT: '_parent',
59
63
  VALIDATION_PARENT_KEY: VALIDATION_PARENT_KEY,
64
+ FLAVOUR: "angular",
60
65
  };
61
66
  /**
62
- * @description Form validation state constants
67
+ * @description Form validation state constants.
63
68
  * @summary Contains constants representing the possible validation states of a form.
64
69
  * These are used to check and handle form validation throughout the application.
70
+ * The VALID state indicates all form controls pass validation, while INVALID
71
+ * indicates one or more validation errors exist.
65
72
  * @typedef {Object} FormConstants
66
73
  * @property {string} VALID - Constant representing a valid form state
67
74
  * @property {string} INVALID - Constant representing an invalid form state
68
75
  * @const FormConstants
69
- * @memberOf module:engine
76
+ * @memberOf module:lib/engine/constants
70
77
  */
71
78
  const FormConstants = {
72
79
  VALID: 'VALID',
73
80
  INVALID: 'INVALID',
74
81
  };
75
82
  /**
76
- * @description Event name constants
77
- * @summary Enum containing constants for event names used throughout the application.
78
- * These are used to standardize event naming and handling.
79
- * @enum {string}
80
- * @readonly
83
+ * @description Event name constants.
84
+ * @summary Contains constants for standardized event names used throughout the application.
85
+ * These constants ensure consistent event naming across components and make it easier to
86
+ * track and handle events. Each constant represents a specific application event type.
87
+ * @typedef {Object} EventConstants
81
88
  * @property {string} BACK_BUTTON_NAVIGATION - Event fired when back button navigation ends
82
- * @property {string} REFRESH_EVENT - Event fired when a refresh action occurs
83
- * @property {string} CLICK_EVENT - Event fired when a click action occurs
84
- * @property {string} SUBMIT_EVENT - Event fired when a form submission occurs
85
- * @memberOf module:engine
89
+ * @property {string} REFRESH - Event fired when a refresh action occurs
90
+ * @property {string} CLICK - Event fired when a click action occurs
91
+ * @property {string} SUBMIT - Event fired when a form submission occurs
92
+ * @property {string} VALIDATION_ERROR - Event fired when a validation error occurs
93
+ * @property {string} FIELDSET_ADD_GROUP - Event fired when adding a group to a fieldset
94
+ * @property {string} FIELDSET_UPDATE_GROUP - Event fired when updating a fieldset group
95
+ * @property {string} FIELDSET_REMOVE_GROUP - Event fired when removing a fieldset group
96
+ * @const EventConstants
97
+ * @memberOf module:lib/engine/constants
86
98
  */
87
99
  const EventConstants = {
88
100
  BACK_BUTTON_NAVIGATION: 'backButtonNavigationEndEvent',
@@ -96,9 +108,11 @@ const EventConstants = {
96
108
  // FIELDSET_GROUP_VALIDATION: 'fieldsetGroupValidationEvent'
97
109
  };
98
110
  /**
99
- * @description Logger level constants
100
- * @summary Enum defining the logging levels used in the application's logging system.
101
- * Lower values represent more verbose logging, while higher values represent more critical logs.
111
+ * @description Logger level constants.
112
+ * @summary Defines the logging levels used in the application's logging system.
113
+ * Lower numeric values represent more verbose logging, while higher values represent
114
+ * more critical logs. These levels control which log messages are output based on
115
+ * the configured logging threshold.
102
116
  * @enum {number}
103
117
  * @readonly
104
118
  * @property {number} ALL - Log everything (most verbose)
@@ -107,7 +121,7 @@ const EventConstants = {
107
121
  * @property {number} WARN - Log warnings
108
122
  * @property {number} ERROR - Log errors
109
123
  * @property {number} CRITICAL - Log critical errors (least verbose)
110
- * @memberOf module:engine
124
+ * @memberOf module:lib/engine/constants
111
125
  */
112
126
  var LoggerLevels;
113
127
  (function (LoggerLevels) {
@@ -120,15 +134,16 @@ var LoggerLevels;
120
134
  })(LoggerLevels || (LoggerLevels = {}));
121
135
  ;
122
136
  /**
123
- * @description Route direction constants
124
- * @summary Enum defining the possible navigation directions in the application.
125
- * Used for controlling navigation flow and animation directions.
137
+ * @description Route direction constants.
138
+ * @summary Defines the possible navigation directions in the application.
139
+ * Used for controlling navigation flow and animation directions during route transitions.
140
+ * These constants help maintain consistent navigation behavior throughout the app.
126
141
  * @enum {string}
127
142
  * @readonly
128
143
  * @property {string} BACK - Navigate back to the previous page
129
144
  * @property {string} FORWARD - Navigate forward to the next page
130
145
  * @property {string} ROOT - Navigate to the root/home page
131
- * @memberOf module:engine
146
+ * @memberOf module:lib/engine/constants
132
147
  */
133
148
  var RouteDirections;
134
149
  (function (RouteDirections) {
@@ -137,15 +152,18 @@ var RouteDirections;
137
152
  RouteDirections["ROOT"] = "root";
138
153
  })(RouteDirections || (RouteDirections = {}));
139
154
  /**
140
- * @description Component tag name constants
141
- * @summary Enum defining the tag names for custom components used in the application.
142
- * These tag names are used for component registration and rendering.
155
+ * @description Component tag name constants.
156
+ * @summary Defines the custom HTML tag names for specialized components used in the application.
157
+ * These tag names are registered with Angular and used for component rendering and identification.
158
+ * Each constant represents the selector for a specific custom component type.
143
159
  * @enum {string}
144
160
  * @readonly
145
161
  * @property {string} LIST_ITEM - Tag name for list item component
146
162
  * @property {string} LIST_INFINITE - Tag name for infinite scrolling list component
147
163
  * @property {string} LIST_PAGINATED - Tag name for paginated list component
148
- * @memberOf module:engine
164
+ * @property {string} CRUD_FIELD - Tag name for CRUD form field component
165
+ * @property {string} LAYOUT_COMPONENT - Tag name for layout container component
166
+ * @memberOf module:lib/engine/constants
149
167
  */
150
168
  var ComponentsTagNames;
151
169
  (function (ComponentsTagNames) {
@@ -156,13 +174,16 @@ var ComponentsTagNames;
156
174
  ComponentsTagNames["LAYOUT_COMPONENT"] = "ngx-decaf-layout";
157
175
  })(ComponentsTagNames || (ComponentsTagNames = {}));
158
176
  /**
159
- * @description Base component property name constants
160
- * @summary Enum defining the standard property names used by base components in the application.
161
- * These property names are used for consistent property access across components.
177
+ * @description Base component property name constants.
178
+ * @summary Defines the standard property names used by base components throughout the application.
179
+ * These constants ensure consistent property naming across components and facilitate
180
+ * property access, validation, and data binding. Used primarily for component input
181
+ * properties and change detection.
162
182
  * @enum {string}
163
183
  * @readonly
164
184
  * @property {string} MODEL - Property name for the component's data model
165
185
  * @property {string} LOCALE - Property name for localization settings
186
+ * @property {string} LOCALE_ROOT - Property name for the locale root identifier
166
187
  * @property {string} PK - Property name for primary key
167
188
  * @property {string} ITEMS - Property name for collection items
168
189
  * @property {string} ROUTE - Property name for routing information
@@ -171,7 +192,10 @@ var ComponentsTagNames;
171
192
  * @property {string} TRANSLATABLE - Property name for translation flag
172
193
  * @property {string} MAPPER - Property name for property mapper
173
194
  * @property {string} INITIALIZED - Property name for initialization state
174
- * @memberOf module:engine
195
+ * @property {string} COMPONENT_NAME - Property name for component identifier
196
+ * @property {string} PARENT_COMPONENT - Property name for parent component reference
197
+ * @property {string} FORM_GROUP_COMPONENT_PROPS - Property name for form group component properties
198
+ * @memberOf module:lib/engine/constants
175
199
  */
176
200
  var BaseComponentProps;
177
201
  (function (BaseComponentProps) {
@@ -190,14 +214,48 @@ var BaseComponentProps;
190
214
  BaseComponentProps["PARENT_COMPONENT"] = "parentComponent";
191
215
  BaseComponentProps["FORM_GROUP_COMPONENT_PROPS"] = "componentProps";
192
216
  })(BaseComponentProps || (BaseComponentProps = {}));
217
+ /**
218
+ * @description List component type constants.
219
+ * @summary Defines the available types for list components, determining their
220
+ * pagination and scrolling behavior. Used to configure list rendering strategies.
221
+ * @enum {string}
222
+ * @readonly
223
+ * @property {string} INFINITE - Infinite scroll list type
224
+ * @property {string} PAGINATED - Paginated list type with page navigation
225
+ * @memberOf module:lib/engine/constants
226
+ */
193
227
  var ListComponentsTypes;
194
228
  (function (ListComponentsTypes) {
195
229
  ListComponentsTypes["INFINITE"] = "infinite";
196
230
  ListComponentsTypes["PAGINATED"] = "paginated";
197
231
  })(ListComponentsTypes || (ListComponentsTypes = {}));
232
+ /**
233
+ * @description CSS class name constants.
234
+ * @summary Contains predefined CSS class names used for consistent styling
235
+ * across components. These constants help maintain a unified visual language
236
+ * and make it easier to apply standard styles.
237
+ * @typedef {Object} CssClasses
238
+ * @property {string} BUTTONS_CONTAINER - CSS class for button container elements
239
+ * @const CssClasses
240
+ * @memberOf module:lib/engine/constants
241
+ */
198
242
  const CssClasses = {
199
243
  BUTTONS_CONTAINER: 'buttons-container',
200
244
  };
245
+ /**
246
+ * @description Default options for reactive CRUD forms.
247
+ * @summary Provides default configuration for form buttons in CRUD operations.
248
+ * Includes default text labels for submit and clear buttons, which can be
249
+ * overridden by individual form implementations.
250
+ * @type {ICrudFormOptions}
251
+ * @property {Object} buttons - Configuration for form action buttons
252
+ * @property {Object} buttons.submit - Submit button configuration
253
+ * @property {string} buttons.submit.text - Default text for submit button
254
+ * @property {Object} buttons.clear - Clear button configuration
255
+ * @property {string} buttons.clear.text - Default text for clear button
256
+ * @const DefaultFormReactiveOptions
257
+ * @memberOf module:lib/engine/constants
258
+ */
201
259
  const DefaultFormReactiveOptions = {
202
260
  buttons: {
203
261
  submit: {
@@ -208,6 +266,30 @@ const DefaultFormReactiveOptions = {
208
266
  },
209
267
  },
210
268
  };
269
+ /**
270
+ * @description Default options for empty list state display.
271
+ * @summary Provides default configuration for displaying empty state in list components
272
+ * when no data is available. Includes default text for title and subtitle, icon name,
273
+ * button text and visibility settings. These defaults can be overridden by individual
274
+ * list component implementations to customize the empty state presentation.
275
+ * @type {IListEmptyOptions}
276
+ * @property {string} title - Default translation key for empty list title
277
+ * @property {string} subtitle - Default translation key for empty list subtitle
278
+ * @property {boolean} showButton - Whether to show action button in empty state
279
+ * @property {string} icon - Default Ionic icon name for empty state
280
+ * @property {string} buttonText - Default translation key for button text
281
+ * @property {string} link - Default navigation link (empty string)
282
+ * @const DefaultListEmptyOptions
283
+ * @memberOf module:lib/engine/constants
284
+ */
285
+ const DefaultListEmptyOptions = {
286
+ title: 'empty.title',
287
+ subtitle: 'empty.subtitle',
288
+ showButton: false,
289
+ icon: 'folder-open-outline',
290
+ buttonText: 'locale.empty.button',
291
+ link: ''
292
+ };
211
293
 
212
294
  /**
213
295
  * @module module:lib/engine/ValidatorFactory
@@ -706,11 +788,17 @@ function generateRandomValue(length = 8, onlyNumbers = false) {
706
788
  return result;
707
789
  }
708
790
  /**
709
- * Converts a string representation of a boolean or a boolean value to a boolean type.
791
+ * @description Converts a string representation of a boolean or a boolean value to a boolean type.
792
+ * @summary This utility function handles conversion of string-based boolean values ('true', 'false')
793
+ * to actual boolean types. It performs case-insensitive string comparison and returns the
794
+ * corresponding boolean value. This is particularly useful when parsing configuration values,
795
+ * URL parameters, or form inputs that may come as strings but need to be used as booleans.
710
796
  *
711
- * @export
712
- * @param {('true' | 'false' | boolean)} prop - The value to convert. Can be the string 'true', 'false', or a boolean.
713
- * @returns {boolean} The boolean representation of the input value. Returns true if the input is the string 'true' or boolean true, false otherwise.
797
+ * @param {'true' | 'false' | boolean} prop - The value to convert. Can be the string 'true', 'false', or a boolean
798
+ * @returns {boolean} The boolean representation of the input value. Returns true if the input is the string 'true' or boolean true, false otherwise
799
+ *
800
+ * @function stringToBoolean
801
+ * @memberOf module:lib/helpers/utils
714
802
  */
715
803
  function stringToBoolean(prop) {
716
804
  if (typeof prop === 'string')
@@ -718,10 +806,18 @@ function stringToBoolean(prop) {
718
806
  return prop;
719
807
  }
720
808
  /**
721
- * Checks if a value is a valid Date object
809
+ * @description Checks if a value is a valid Date object.
810
+ * @summary This validation function determines whether a given value represents a valid date.
811
+ * It handles multiple input types including Date objects, timestamp numbers, and date strings.
812
+ * For string inputs, it supports ISO 8601 format (YYYY-MM-DD) with or without time components.
813
+ * The function performs comprehensive validation including regex pattern matching and Date
814
+ * object creation to ensure the date is not only parseable but also represents a real date.
815
+ *
816
+ * @param {string | Date | number} date - The value to check. Can be a Date object, a timestamp number, or a date string
817
+ * @returns {boolean} Returns true if the value is a valid Date object (not NaN), otherwise false
722
818
  *
723
- * @param {(string | Date | number)} date - The value to check. Can be a Date object, a timestamp number, or a date string
724
- * @return {boolean} Returns true if the value is a valid Date object (not NaN), otherwise false
819
+ * @function isValidDate
820
+ * @memberOf module:lib/helpers/utils
725
821
  */
726
822
  function isValidDate(date) {
727
823
  try {
@@ -741,12 +837,21 @@ function isValidDate(date) {
741
837
  }
742
838
  }
743
839
  /**
744
- * Formats a date into a localized string representation
840
+ * @description Formats a date into a localized string representation.
841
+ * @summary This function converts a date value into a formatted string according to a specified
842
+ * or system locale. It accepts multiple input formats (Date objects, timestamps, or date strings)
843
+ * and returns a consistently formatted date string in DD/MM/YYYY format. If the input date is
844
+ * invalid, the function returns the original input as a string. The function automatically
845
+ * uses the system locale if none is provided and handles string format conversions by replacing
846
+ * forward slashes with hyphens for proper Date parsing.
745
847
  *
746
- * @param {(string | Date | number)} date - The date to format. Can be a Date object, a timestamp number, or a date string
848
+ * @param {string | Date | number} date - The date to format. Can be a Date object, a timestamp number, or a date string
747
849
  * @param {string} [locale] - The locale to use for formatting. If not provided, the system's locale will be used
748
- * @return {(Date | string)} A formatted date string in the format DD/MM/YYYY according to the specified locale,
749
- * or the original input as a string if the date is invalid
850
+ * @returns {Date | string} A formatted date string in the format DD/MM/YYYY according to the specified locale,
851
+ * or the original input as a string if the date is invalid
852
+ *
853
+ * @function formatDate
854
+ * @memberOf module:lib/helpers/utils
750
855
  */
751
856
  function formatDate(date, locale) {
752
857
  if (!locale)
@@ -763,12 +868,21 @@ function formatDate(date, locale) {
763
868
  return r;
764
869
  }
765
870
  /**
766
- * Attempts to parse a date string, Date object, or number into a valid Date object
871
+ * @description Attempts to parse a date string, Date object, or number into a valid Date object.
872
+ * @summary This function provides robust date parsing functionality that handles the specific
873
+ * format "DD/MM/YYYY HH:MM:SS:MS". It first validates the input date, and if already valid,
874
+ * returns it as-is. For string inputs, it parses the date and time components separately,
875
+ * extracts numeric values, and constructs a new Date object. The function includes validation
876
+ * to ensure the resulting Date object is valid and logs a warning if parsing fails.
877
+ * Returns null for invalid or unsupported date formats.
878
+ *
879
+ * @param {string | Date | number} date - The date to parse. Can be a Date object, a timestamp number,
880
+ * or a date string in the format "DD/MM/YYYY HH:MM:SS:MS"
881
+ * @returns {Date | null} A valid Date object if parsing is successful, or null if the date is invalid
882
+ * or doesn't match the expected format
767
883
  *
768
- * @param {(string | Date | number)} date - The date to parse. Can be a Date object, a timestamp number,
769
- * or a date string in the format "DD/MM/YYYY HH:MM:SS:MS"
770
- * @return {(Date | null)} A valid Date object if parsing is successful, or null if the date is invalid
771
- * or doesn't match the expected format
884
+ * @function parseToValidDate
885
+ * @memberOf module:lib/helpers/utils
772
886
  */
773
887
  function parseToValidDate(date) {
774
888
  if (isValidDate(date))
@@ -786,13 +900,22 @@ function parseToValidDate(date) {
786
900
  return date;
787
901
  }
788
902
  /**
789
- * Maps an item object using a provided mapper object and optional additional properties.
903
+ * @description Maps an item object using a provided mapper object and optional additional properties.
904
+ * @summary This function transforms a source object into a new object based on mapping rules defined
905
+ * in the mapper parameter. It supports dot notation for nested property access (e.g., 'user.name.first')
906
+ * and handles various data types including strings and complex objects. For date values, it automatically
907
+ * formats them using the formatDate function. The function also allows merging additional properties
908
+ * into the result. When a mapped value is null or undefined, it uses the original mapper value as
909
+ * a fallback.
790
910
  *
791
- * @param {KeyValue} item - The source object to be mapped.
911
+ * @param {KeyValue} item - The source object to be mapped
792
912
  * @param {KeyValue} mapper - An object that defines the mapping rules. Keys represent the new property names,
793
- * and values represent the path to the corresponding values in the source object.
794
- * @param {KeyValue} [props] - Optional additional properties to be included in the mapped object.
795
- * @returns {KeyValue} A new object with properties mapped according to the mapper object and including any additional properties.
913
+ * and values represent the path to the corresponding values in the source object
914
+ * @param {KeyValue} [props] - Optional additional properties to be included in the mapped object
915
+ * @returns {KeyValue} A new object with properties mapped according to the mapper object and including any additional properties
916
+ *
917
+ * @function itemMapper
918
+ * @memberOf module:lib/helpers/utils
796
919
  */
797
920
  function itemMapper(item, mapper, props) {
798
921
  return Object.entries(mapper).reduce((accum, [key, value]) => {
@@ -819,15 +942,23 @@ function itemMapper(item, mapper, props) {
819
942
  }, {});
820
943
  }
821
944
  /**
822
- * Maps an array of data objects using a provided mapper object.
945
+ * @description Maps an array of data objects using a provided mapper object.
946
+ * @summary This function transforms an array of objects by applying mapping rules to each item
947
+ * using the itemMapper function. It processes each element in the data array and creates
948
+ * new mapped objects based on the mapper configuration. The function includes validation
949
+ * to ensure meaningful data: if a mapped item contains only null/undefined values, it
950
+ * preserves the original item instead. This prevents data loss during transformation.
951
+ * Returns an empty array if the input data is null, undefined, or empty.
823
952
  *
824
- * @template T - The type of the resulting mapped items.
825
- * @param {any[]} data - The array of data objects to be mapped.
826
- * @param {KeyValue} mapper - An object that defines the mapping rules.
827
- * @param {KeyValue} [props] - Additional properties to be included in the mapped items.
953
+ * @template T - The type of the resulting mapped items
954
+ * @param {T[]} data - The array of data objects to be mapped
955
+ * @param {KeyValue} mapper - An object that defines the mapping rules
956
+ * @param {KeyValue} [props] - Additional properties to be included in the mapped items
957
+ * @returns {T[]} The array of mapped items. If an item in the original array does not have any non-null values after mapping,
958
+ * the original item is returned instead
828
959
  *
829
- * @returns {T[]} - The array of mapped items. If an item in the original array does not have any non-null values after mapping,
830
- * the original item is returned instead.
960
+ * @function dataMapper
961
+ * @memberOf module:lib/helpers/utils
831
962
  */
832
963
  function dataMapper(data, mapper, props) {
833
964
  if (!data || !data.length)
@@ -906,13 +1037,11 @@ async function isDarkMode() {
906
1037
  */
907
1038
 
908
1039
  /**
909
- * @module module:lib/engine/NgxDecafFormService
1040
+ * @module lib/engine/NgxDecafFormService
910
1041
  * @description Utilities to create and manage Angular forms in Decaf components.
911
1042
  * @summary The NgxDecafFormService exposes helpers to build FormGroup/FormArray instances
912
1043
  * from component metadata or UI model definitions, register forms in a registry,
913
1044
  * validate and extract form data, and create controls with appropriate validators.
914
- *
915
- * @link {@link NgxDecafFormService}
916
1045
  */
917
1046
  /**
918
1047
  * @description Service for managing Angular forms and form controls.
@@ -935,7 +1064,6 @@ async function isDarkMode() {
935
1064
  *
936
1065
  * // Getting form data
937
1066
  * const formData = NgxDecafFormService.getFormData(form);
938
- *
939
1067
  * @mermaid
940
1068
  * sequenceDiagram
941
1069
  * participant C as Component
@@ -957,46 +1085,42 @@ class NgxDecafFormService {
957
1085
  * @description WeakMap that stores control properties for form controls.
958
1086
  * @summary A WeakMap that associates AbstractControl instances with their corresponding FieldProperties.
959
1087
  * This allows the service to track metadata for form controls without creating memory leaks.
960
- *
961
1088
  * @type {WeakMap<AbstractControl, FieldProperties>}
962
1089
  * @private
963
1090
  * @static
964
- * @memberOf NgxDecafFormService
965
1091
  */
966
1092
  static { this.controls = new WeakMap(); }
967
1093
  /**
968
1094
  * @description Registry of form groups indexed by their unique identifiers.
969
1095
  * @summary A Map that stores FormGroup instances with their unique string identifiers.
970
1096
  * This allows global access to registered forms throughout the application.
971
- *
972
1097
  * @type {Map<string, FormGroup>}
973
1098
  * @private
974
1099
  * @static
975
- * @memberOf NgxDecafFormService
976
1100
  */
977
1101
  static { this.formRegistry = new Map(); }
978
1102
  /**
979
1103
  * @description Creates a new form group or form array with the specified identifier.
980
- * @summary Generates a FormGroup or FormArray based on the provided properties. If pages are specified
981
- * and greater than 1, creates a FormArray; otherwise creates a FormGroup. The form can optionally
982
- * be registered in the global form registry for later access throughout the application.
983
- *
1104
+ * @summary Generates a FormGroup or FormArray based on the provided parameters. If formArray is true,
1105
+ * creates a FormArray; otherwise creates a FormGroup. The form can optionally be registered in the
1106
+ * global form registry for later access throughout the application. If a form with the given id
1107
+ * already exists in the registry, it returns the existing form.
984
1108
  * @param {string} id - Unique identifier for the form
985
- * @param {Partial<IComponentInput>} [props={}] - Configuration properties for the form
1109
+ * @param {boolean} [formArray=false] - Whether to create a FormArray instead of a FormGroup
986
1110
  * @param {boolean} [registry=true] - Whether to register the form in the global registry
987
- * @return {FormGroup | FormArray} The created form instance
988
- *
1111
+ * @return {FormGroup | FormArray} The created or existing form instance
989
1112
  * @mermaid
990
1113
  * sequenceDiagram
991
1114
  * participant C as Component
992
1115
  * participant NFS as NgxDecafFormService
993
1116
  * participant FR as Form Registry
994
1117
  * participant AF as Angular Forms
995
- *
996
- * C->>NFS: createForm(id, props, registry)
1118
+ * C->>NFS: createForm(id, formArray, registry)
997
1119
  * NFS->>FR: Check if form exists
998
- * alt Form doesn't exist
999
- * alt props.pages > 1
1120
+ * alt Form exists
1121
+ * FR-->>NFS: Return existing form
1122
+ * else Form doesn't exist
1123
+ * alt formArray is true
1000
1124
  * NFS->>AF: new FormArray([])
1001
1125
  * else
1002
1126
  * NFS->>AF: new FormGroup({})
@@ -1006,9 +1130,7 @@ class NgxDecafFormService {
1006
1130
  * end
1007
1131
  * end
1008
1132
  * NFS-->>C: Return FormGroup | FormArray
1009
- *
1010
1133
  * @static
1011
- * @memberOf NgxDecafFormService
1012
1134
  */
1013
1135
  static createForm(id, formArray = false, registry = true) {
1014
1136
  const form = this.formRegistry.get(id) ?? (formArray ? new FormArray([]) : new FormGroup({}));
@@ -1021,20 +1143,26 @@ class NgxDecafFormService {
1021
1143
  * @summary Registers a FormGroup or FormArray with a unique identifier for global access throughout
1022
1144
  * the application. This allows forms to be retrieved and managed centrally. Throws an error if
1023
1145
  * the identifier is already in use to prevent conflicts.
1024
- *
1025
1146
  * @param {string} formId - The unique identifier for the form
1026
1147
  * @param {FormParent} formGroup - The FormGroup or FormArray to be registered
1027
1148
  * @return {void}
1028
1149
  * @throws {Error} If a FormGroup with the given id is already registered
1029
- *
1030
1150
  * @static
1031
- * @memberOf NgxDecafFormService
1032
1151
  */
1033
1152
  static addRegistry(formId, formGroup) {
1034
1153
  if (this.formRegistry.has(formId))
1035
1154
  throw new Error(`A FormGroup with id '${formId}' is already registered.`);
1036
1155
  this.formRegistry.set(formId, formGroup);
1037
1156
  }
1157
+ /**
1158
+ * @description Retrieves a form from the registry by its identifier.
1159
+ * @summary Gets a FormGroup or FormArray from the registry using its unique identifier.
1160
+ * Returns undefined if the form is not found in the registry. This method provides
1161
+ * safe access to registered forms without throwing errors.
1162
+ * @param {string} [id] - The unique identifier of the form to retrieve
1163
+ * @return {FormParent | undefined} The FormGroup or FormArray if found, undefined otherwise
1164
+ * @static
1165
+ */
1038
1166
  static getOnRegistry(id) {
1039
1167
  return this.formRegistry.get(id);
1040
1168
  }
@@ -1043,12 +1171,9 @@ class NgxDecafFormService {
1043
1171
  * @summary Deletes a FormGroup or FormArray from the registry using its unique identifier.
1044
1172
  * This cleans up the registry and allows the identifier to be reused. The form itself
1045
1173
  * is not destroyed, only removed from the central registry.
1046
- *
1047
1174
  * @param {string} formId - The unique identifier of the form to be removed
1048
1175
  * @return {void}
1049
- *
1050
1176
  * @static
1051
- * @memberOf NgxDecafFormService
1052
1177
  */
1053
1178
  static removeRegistry(formId) {
1054
1179
  this.formRegistry.delete(formId);
@@ -1058,20 +1183,17 @@ class NgxDecafFormService {
1058
1183
  * @summary Traverses the form group structure to find the parent group and control name for a given path.
1059
1184
  * Handles complex nested structures including arrays and sub-groups. Creates missing intermediate
1060
1185
  * groups as needed and properly configures FormArray controls for multiple value scenarios.
1061
- *
1062
1186
  * @param {FormGroup} formGroup - The root FormGroup to traverse
1063
1187
  * @param {string} path - The dot-separated path to the control (e.g., 'user.address.street')
1064
1188
  * @param {IComponentInput} componentProps - Properties defining the component configuration
1065
1189
  * @param {KeyValue} parentProps - Properties from the parent component for context
1066
1190
  * @return {FormParentGroup} A tuple containing the parent FormGroup and the control name
1067
- *
1068
1191
  * @private
1069
1192
  * @mermaid
1070
1193
  * sequenceDiagram
1071
1194
  * participant NFS as NgxDecafFormService
1072
1195
  * participant FG as FormGroup
1073
1196
  * participant FA as FormArray
1074
- *
1075
1197
  * NFS->>NFS: Split path into parts
1076
1198
  * loop For each path part
1077
1199
  * alt Control doesn't exist
@@ -1085,9 +1207,7 @@ class NgxDecafFormService {
1085
1207
  * NFS->>NFS: Navigate to next level
1086
1208
  * end
1087
1209
  * NFS-->>NFS: Return [parentGroup, controlName]
1088
- *
1089
1210
  * @static
1090
- * @memberOf NgxDecafFormService
1091
1211
  */
1092
1212
  static resolveParentGroup(formGroup, path, componentProps, parentProps) {
1093
1213
  const isMultiple = parentProps?.multiple || parentProps?.type === Array.name || false;
@@ -1134,14 +1254,11 @@ class NgxDecafFormService {
1134
1254
  * @description Retrieves component properties from a FormGroup or FormArray.
1135
1255
  * @summary Extracts component properties stored in the form group metadata. If a FormGroup is provided
1136
1256
  * and groupArrayName is specified, it will look for the FormArray within the form structure.
1137
- *
1138
1257
  * @param {FormGroup | FormArray} formGroup - The form group or form array to extract properties from
1139
1258
  * @param {string} [key] - Optional key to retrieve a specific property
1140
1259
  * @param {string} [groupArrayName] - Optional name of the group array if formGroup is not a FormArray
1141
1260
  * @return {Partial<FieldProperties>} The component properties or a specific property if key is provided
1142
- *
1143
1261
  * @static
1144
- * @memberOf NgxDecafFormService
1145
1262
  */
1146
1263
  static getComponentPropsFromGroupArray(formGroup, key, groupArrayName) {
1147
1264
  if (!(formGroup instanceof FormArray) && typeof groupArrayName === Primitives.STRING)
@@ -1153,14 +1270,12 @@ class NgxDecafFormService {
1153
1270
  * @description Adds a new group to a parent FormArray.
1154
1271
  * @summary Creates and adds a new FormGroup to the specified parent FormArray based on the
1155
1272
  * component properties stored in the parent's metadata. This is used for dynamic form arrays
1156
- * where new groups need to be added at runtime.
1157
- *
1158
- * @param {FormParent} parentForm - The root form group containing the parent FormArray
1159
- * @param {number} index - The index position where the new group should be added
1160
- * @return {FormGroup} The newly created and added FormGroup
1161
- *
1273
+ * where new groups need to be added at runtime. Clones the control at the specified index
1274
+ * to maintain the same structure and validators.
1275
+ * @param {FormParent} parentForm - The FormArray or FormGroup containing the parent FormArray
1276
+ * @param {number} [index] - The index position to clone from; defaults to last index if length > 0, otherwise 0
1277
+ * @return {FormArray} The parent FormArray after adding the new group
1162
1278
  * @static
1163
- * @memberOf NgxDecafFormService
1164
1279
  */
1165
1280
  static addGroupToParent(parentForm, index) {
1166
1281
  if (parentForm instanceof FormGroup)
@@ -1173,14 +1288,11 @@ class NgxDecafFormService {
1173
1288
  * @description Retrieves a FormGroup from a parent FormArray at the specified index.
1174
1289
  * @summary Gets a FormGroup from the specified parent FormArray. If the group doesn't exist
1175
1290
  * at the given index, it will create a new one using addGroupToParent.
1176
- *
1177
- * @param {FormGroup} formGroup - The root form group containing the parent FormArray
1291
+ * @param {FormParent} formGroup - The root form group containing the parent FormArray
1178
1292
  * @param {string} parentName - The name of the parent FormArray to retrieve the group from
1179
1293
  * @param {number} [index=1] - The index of the group to retrieve
1180
1294
  * @return {FormGroup} The FormGroup at the specified index
1181
- *
1182
1295
  * @static
1183
- * @memberOf NgxDecafFormService
1184
1296
  */
1185
1297
  static getGroupFromParent(formGroup, parentName, index = 1) {
1186
1298
  const childGroup = (formGroup.get(parentName) || formGroup).at(index);
@@ -1188,6 +1300,17 @@ class NgxDecafFormService {
1188
1300
  return childGroup;
1189
1301
  return this.addGroupToParent(formGroup, index).at(index);
1190
1302
  }
1303
+ /**
1304
+ * @description Clones a form control with its validators.
1305
+ * @summary Creates a deep copy of a FormControl, FormGroup, or FormArray, preserving
1306
+ * validators but resetting values and state. This is useful for creating new instances
1307
+ * of form controls with the same validation rules, particularly in dynamic FormArrays
1308
+ * where new groups need to be added with identical structure.
1309
+ * @param {AbstractControl} control - The control to clone (FormControl, FormGroup, or FormArray)
1310
+ * @return {AbstractControl} A new instance of the control with the same validators
1311
+ * @throws {Error} If the control type is not supported
1312
+ * @static
1313
+ */
1191
1314
  static cloneFormControl(control) {
1192
1315
  const syncValidators = (control.validator ? [control.validator] : []).filter(fn => {
1193
1316
  // if(lastIndex > 0)
@@ -1225,14 +1348,13 @@ class NgxDecafFormService {
1225
1348
  * @description Checks if a value is unique within a FormArray group.
1226
1349
  * @summary Validates that the primary key value in a FormGroup is unique among all groups
1227
1350
  * in the parent FormArray. The uniqueness check behavior differs based on the operation type.
1228
- *
1351
+ * For both CREATE and UPDATE operations, it checks that no other group in the array has the same
1352
+ * primary key value.
1229
1353
  * @param {FormGroup} formGroup - The FormGroup to check for uniqueness
1230
- * @param {number} index - The index of the current group within the FormArray
1231
1354
  * @param {OperationKeys} [operation=OperationKeys.CREATE] - The type of operation being performed
1355
+ * @param {number} [index] - The index of the current group within the FormArray
1232
1356
  * @return {boolean} True if the value is unique, false otherwise
1233
- *
1234
1357
  * @static
1235
- * @memberOf NgxDecafFormService
1236
1358
  */
1237
1359
  static isUniqueOnGroup(formGroup, operation = OperationKeys.CREATE, index) {
1238
1360
  const formArray = formGroup.parent;
@@ -1258,12 +1380,9 @@ class NgxDecafFormService {
1258
1380
  * @description Enables all controls within a FormGroup or FormArray.
1259
1381
  * @summary Recursively enables all form controls within the provided FormGroup or FormArray.
1260
1382
  * This is useful for making all controls interactive after they have been disabled.
1261
- *
1262
1383
  * @param {FormArray | FormGroup} formGroup - The FormGroup or FormArray to enable all controls for
1263
1384
  * @return {void}
1264
- *
1265
1385
  * @static
1266
- * @memberOf NgxDecafFormService
1267
1386
  */
1268
1387
  static enableAllGroupControls(formGroup) {
1269
1388
  Object.keys(formGroup.controls).forEach(key => {
@@ -1282,17 +1401,15 @@ class NgxDecafFormService {
1282
1401
  * @description Adds a form control to a form group based on component properties.
1283
1402
  * @summary Creates and configures a FormControl within the specified FormGroup using the provided
1284
1403
  * component properties. Handles nested paths, multiple controls (FormArrays), and control registration.
1285
- * This method supports complex form structures with nested groups and arrays.
1286
- *
1287
- * @param {FormGroup} formGroup - The form group to add the control to
1404
+ * This method supports complex form structures with nested groups and arrays. It also manages
1405
+ * page-based forms and FormArray indexing.
1406
+ * @param {FormParent} formGroup - The form group or form array to add the control to
1288
1407
  * @param {IComponentInput} componentProps - The component properties defining the control configuration
1289
- * @param {KeyValue} [parentProps={}] - Properties from the parent component for context
1408
+ * @param {Partial<IComponentInput>} [parentProps={}] - Properties from the parent component for context
1290
1409
  * @param {number} [index=0] - The index for multiple controls in FormArrays
1291
- * @return {void}
1292
- *
1410
+ * @return {FormParent} The updated form parent (FormGroup or FormArray)
1293
1411
  * @private
1294
1412
  * @static
1295
- * @memberOf NgxDecafFormService
1296
1413
  */
1297
1414
  static addFormControl(formGroup, componentProps, parentProps = {}, index = 0) {
1298
1415
  const isMultiple = parentProps?.['multiple'] || parentProps?.['type'] === 'Array' || false;
@@ -1328,18 +1445,15 @@ class NgxDecafFormService {
1328
1445
  * @summary Finds and returns an AbstractControl from a registered form using the form id and optional path.
1329
1446
  * This method provides centralized access to form controls across the application by leveraging
1330
1447
  * the form registry system.
1331
- *
1332
1448
  * @param {string} formId - The unique identifier of the form in the registry
1333
1449
  * @param {string} [path] - The optional dot-separated path to a specific control within the form
1334
1450
  * @return {AbstractControl} The requested AbstractControl (FormGroup, FormArray, or FormControl)
1335
1451
  * @throws {Error} If the form is not found in the registry or the control is not found in the form
1336
- *
1337
1452
  * @mermaid
1338
1453
  * sequenceDiagram
1339
1454
  * participant C as Component
1340
1455
  * participant NFS as NgxDecafFormService
1341
1456
  * participant FR as Form Registry
1342
- *
1343
1457
  * C->>NFS: getControlFromForm(formId, path?)
1344
1458
  * NFS->>FR: Get form by formId
1345
1459
  * alt Form not found
@@ -1358,9 +1472,7 @@ class NgxDecafFormService {
1358
1472
  * NFS-->>C: Return form
1359
1473
  * end
1360
1474
  * end
1361
- *
1362
1475
  * @static
1363
- * @memberOf NgxDecafFormService
1364
1476
  */
1365
1477
  static getControlFromForm(formId, path) {
1366
1478
  const form = this.formRegistry.get(formId);
@@ -1378,18 +1490,15 @@ class NgxDecafFormService {
1378
1490
  * @summary Generates a FormGroup from an array of UIModelMetadata objects, extracting component
1379
1491
  * properties and creating appropriate form controls. This method is specifically designed to work
1380
1492
  * with the UI decorator system and provides automatic form generation from metadata.
1381
- *
1382
1493
  * @param {string} id - Unique identifier for the form
1383
1494
  * @param {boolean} [registry=false] - Whether to register the created form in the global registry
1384
1495
  * @param {UIModelMetadata[]} [children] - Array of UI model metadata objects to create controls from
1385
1496
  * @return {FormGroup} The created FormGroup with controls for each child metadata
1386
- *
1387
1497
  * @mermaid
1388
1498
  * sequenceDiagram
1389
1499
  * participant C as Component
1390
1500
  * participant NFS as NgxDecafFormService
1391
1501
  * participant AF as Angular Forms
1392
- *
1393
1502
  * C->>NFS: createFormFromChildren(id, registry, children)
1394
1503
  * NFS->>AF: new FormGroup({})
1395
1504
  * loop For each child metadata
@@ -1400,9 +1509,7 @@ class NgxDecafFormService {
1400
1509
  * NFS->>NFS: addRegistry(id, form)
1401
1510
  * end
1402
1511
  * NFS-->>C: Return FormGroup
1403
- *
1404
1512
  * @static
1405
- * @memberOf NgxDecafFormService
1406
1513
  */
1407
1514
  static createFormFromChildren(id, registry = false, children) {
1408
1515
  const form = new FormGroup({});
@@ -1419,18 +1526,15 @@ class NgxDecafFormService {
1419
1526
  * @summary Generates a FormGroup based on an array of component configurations and optionally registers it.
1420
1527
  * This method processes component input configurations to create appropriate form controls with
1421
1528
  * validation and initial values.
1422
- *
1423
1529
  * @param {string} id - The unique identifier for the form
1424
1530
  * @param {IComponentConfig[]} components - An array of component configurations defining the form structure
1425
1531
  * @param {boolean} [registry=false] - Whether to register the created form in the global registry
1426
1532
  * @return {FormGroup} The created FormGroup with controls for each component configuration
1427
- *
1428
1533
  * @mermaid
1429
1534
  * sequenceDiagram
1430
1535
  * participant C as Component
1431
1536
  * participant NFS as NgxDecafFormService
1432
1537
  * participant AF as Angular Forms
1433
- *
1434
1538
  * C->>NFS: createFormFromComponents(id, components, registry)
1435
1539
  * NFS->>AF: new FormGroup({})
1436
1540
  * loop For each component config
@@ -1441,9 +1545,7 @@ class NgxDecafFormService {
1441
1545
  * NFS->>NFS: addRegistry(id, form)
1442
1546
  * end
1443
1547
  * NFS-->>C: Return FormGroup
1444
- *
1445
1548
  * @static
1446
- * @memberOf NgxDecafFormService
1447
1549
  */
1448
1550
  static createFormFromComponents(id, components, registry = false) {
1449
1551
  const form = new FormGroup({});
@@ -1458,54 +1560,51 @@ class NgxDecafFormService {
1458
1560
  * @description Adds a control to a form based on component properties.
1459
1561
  * @summary Creates and adds a form control to a form (existing or new) based on the provided component properties.
1460
1562
  * Handles multi-page forms by managing FormArray structures and proper indexing. This method supports
1461
- * complex form scenarios including nested controls and page-based form organization.
1462
- *
1563
+ * complex form scenarios including nested controls and page-based form organization. It automatically
1564
+ * creates FormArrays for forms with multiple pages and manages page indexing.
1463
1565
  * @param {string} id - The unique identifier of the form
1464
1566
  * @param {FieldProperties} componentProperties - The properties of the component to create the control from
1465
1567
  * @param {FieldProperties} [parentProps] - Optional parent properties for context and configuration
1466
- * @return {AbstractControl} The form or created control
1467
- *
1568
+ * @return {FormParent} The form or created control (FormGroup or FormArray)
1569
+ * @throws {Error} If page property is required but not provided or is invalid
1468
1570
  * @mermaid
1469
1571
  * sequenceDiagram
1470
1572
  * participant C as Component
1471
1573
  * participant NFS as NgxDecafFormService
1472
1574
  * participant F as Form
1473
- *
1474
1575
  * C->>NFS: addControlFromProps(id, componentProps, parentProps?)
1475
- * NFS->>NFS: createForm(id, parentProps, true)
1476
- * alt Multi-page form (parentProps.pages > 1)
1576
+ * NFS->>NFS: createForm(id, formArray, true)
1577
+ * alt Multi-page form (parentProps.pages > 0)
1477
1578
  * NFS->>NFS: Calculate page index
1478
- * NFS->>F: Get or create FormGroup at index
1579
+ * alt Group doesn't exist at index
1580
+ * NFS->>F: Create new FormGroup at index
1581
+ * end
1479
1582
  * NFS->>NFS: Set form to page FormGroup
1480
1583
  * end
1481
1584
  * alt componentProperties has path
1482
1585
  * NFS->>NFS: addFormControl(form, componentProperties, parentProps)
1483
1586
  * end
1484
1587
  * NFS-->>C: Return form/control
1485
- *
1486
1588
  * @static
1487
- * @memberOf NgxDecafFormService
1488
1589
  */
1489
1590
  static addControlFromProps(id, componentProperties, parentProps) {
1490
1591
  const formArray = (componentProperties?.pages && componentProperties?.pages >= 1 || componentProperties.multiple === true);
1491
1592
  let form = this.createForm(id, formArray, true);
1492
- const formLength = form.length;
1493
1593
  if (parentProps?.pages && parentProps?.pages > 0) {
1494
- let index = componentProperties.page || parentProps.page;
1594
+ const index = componentProperties.page || parentProps.page;
1495
1595
  if (!(typeof index === 'number') || index === 0)
1496
1596
  throw Error(`Property 'page' is required and greather than 0 on ${componentProperties.name}`);
1497
- if (index > formLength) {
1498
- if (form?.['lastIndex'] && index === form['lastIndex']['page']) {
1499
- index = form['lastIndex']['index'];
1500
- }
1501
- else {
1502
- form['lastIndex'] = {
1503
- page: index,
1504
- index: formLength + 1
1505
- };
1506
- index = formLength + 1;
1507
- }
1508
- }
1597
+ // if(index > formLength) {
1598
+ // if((form as KeyValue)?.['lastIndex'] && index === (form as KeyValue)['lastIndex']['page']) {
1599
+ // index = (form as KeyValue)['lastIndex']['index'];
1600
+ // } else {
1601
+ // (form as KeyValue)['lastIndex'] = {
1602
+ // page: index,
1603
+ // index: formLength + 1
1604
+ // };
1605
+ // index = formLength + 1;
1606
+ // }
1607
+ // }
1509
1608
  let group = form.controls[index - 1];
1510
1609
  if (!group) {
1511
1610
  group = new FormGroup({});
@@ -1522,17 +1621,14 @@ class NgxDecafFormService {
1522
1621
  * @summary Extracts and processes the data from a FormGroup, handling different input types and nested form groups.
1523
1622
  * Performs type conversion for various HTML5 input types, validates nested controls, and manages
1524
1623
  * multiple control scenarios. Automatically enables all group controls after data extraction.
1525
- *
1526
1624
  * @param {FormGroup} formGroup - The FormGroup to extract data from
1527
1625
  * @return {Record<string, unknown>} An object containing the processed form data with proper type conversions
1528
- *
1529
1626
  * @mermaid
1530
1627
  * sequenceDiagram
1531
1628
  * participant C as Component
1532
1629
  * participant NFS as NgxDecafFormService
1533
1630
  * participant FG as FormGroup
1534
1631
  * participant FC as FormControl
1535
- *
1536
1632
  * C->>NFS: getFormData(formGroup)
1537
1633
  * loop For each control in formGroup
1538
1634
  * alt Control is not FormControl
@@ -1556,9 +1652,7 @@ class NgxDecafFormService {
1556
1652
  * end
1557
1653
  * NFS->>NFS: enableAllGroupControls(formGroup)
1558
1654
  * NFS-->>C: Return processed data object
1559
- *
1560
1655
  * @static
1561
- * @memberOf NgxDecafFormService
1562
1656
  */
1563
1657
  static getFormData(formGroup) {
1564
1658
  const data = {};
@@ -1609,13 +1703,11 @@ class NgxDecafFormService {
1609
1703
  * @summary Recursively validates all fields in a form control or form group, marking them as touched and dirty.
1610
1704
  * Performs comprehensive validation including uniqueness checks for primary keys in FormArray scenarios.
1611
1705
  * This method ensures all validation rules are applied and form state is properly updated.
1612
- *
1613
1706
  * @param {AbstractControl} control - The control or form group to validate
1614
1707
  * @param {string} [pk] - Optional primary key field name for uniqueness validation
1615
1708
  * @param {string} [path] - The path to the control within the form for error reporting
1616
1709
  * @return {boolean} True if all fields are valid, false otherwise
1617
1710
  * @throws {Error} If no control is found at the specified path or if the control type is unknown
1618
- *
1619
1711
  * @mermaid
1620
1712
  * sequenceDiagram
1621
1713
  * participant C as Component
@@ -1623,7 +1715,6 @@ class NgxDecafFormService {
1623
1715
  * participant FC as FormControl
1624
1716
  * participant FG as FormGroup
1625
1717
  * participant FA as FormArray
1626
- *
1627
1718
  * C->>NFS: validateFields(control, pk?, path?)
1628
1719
  * alt Control is FormControl
1629
1720
  * NFS->>FC: markAsTouched()
@@ -1649,9 +1740,7 @@ class NgxDecafFormService {
1649
1740
  * else Unknown control type
1650
1741
  * NFS-->>C: Throw Error
1651
1742
  * end
1652
- *
1653
1743
  * @static
1654
- * @memberOf NgxDecafFormService
1655
1744
  */
1656
1745
  static validateFields(control, pk, path) {
1657
1746
  control = path ? control.get(path) : control;
@@ -1702,8 +1791,12 @@ class NgxDecafFormService {
1702
1791
  /**
1703
1792
  * @description Generates validators from component properties.
1704
1793
  * @summary Creates an array of ValidatorFn based on the supported validation keys in the component properties.
1705
- * @param {KeyValue} props - The component properties.
1706
- * @return {ValidatorFn[]} An array of validator functions.
1794
+ * Maps each validation property to its corresponding Angular validator function using the ValidatorFactory.
1795
+ * Only processes properties that are recognized as validation keys by the Validation utility.
1796
+ * @param {KeyValue} props - The component properties containing validation rules
1797
+ * @return {ValidatorFn[]} An array of validator functions
1798
+ * @private
1799
+ * @static
1707
1800
  */
1708
1801
  static validatorsFromProps(props) {
1709
1802
  const supportedValidationKeys = Validation.keys();
@@ -1718,18 +1811,15 @@ class NgxDecafFormService {
1718
1811
  * @summary Generates a FormControl with validators and initial configuration based on the provided
1719
1812
  * component properties. Handles different input types, sets initial values, and configures
1720
1813
  * validation rules and update modes.
1721
- *
1722
1814
  * @param {FieldProperties} props - The component properties defining the control configuration
1723
1815
  * @param {FieldUpdateMode} [updateMode='change'] - The update mode for the control ('change', 'blur', 'submit')
1724
1816
  * @return {FormControl} The created FormControl with proper configuration and validators
1725
- *
1726
1817
  * @mermaid
1727
1818
  * sequenceDiagram
1728
1819
  * participant C as Component
1729
1820
  * participant NFS as NgxDecafFormService
1730
1821
  * participant VF as ValidatorFactory
1731
1822
  * participant AF as Angular Forms
1732
- *
1733
1823
  * C->>NFS: fromProps(props, updateMode?)
1734
1824
  * NFS->>NFS: validatorsFromProps(props)
1735
1825
  * NFS->>VF: Create validators from props
@@ -1744,9 +1834,7 @@ class NgxDecafFormService {
1744
1834
  * NFS->>AF: new FormControl(config)
1745
1835
  * AF-->>NFS: Return FormControl
1746
1836
  * NFS-->>C: Return configured FormControl
1747
- *
1748
1837
  * @static
1749
- * @memberOf NgxDecafFormService
1750
1838
  */
1751
1839
  static fromProps(props, updateMode = 'change') {
1752
1840
  const validators = this.validatorsFromProps(props);
@@ -1770,12 +1858,9 @@ class NgxDecafFormService {
1770
1858
  * @summary Gets the FieldProperties associated with a form control from the internal WeakMap.
1771
1859
  * This method provides access to the original component properties that were used to create
1772
1860
  * the control, enabling validation, rendering, and behavior configuration.
1773
- *
1774
1861
  * @param {FormControl | FormArray | FormGroup} control - The form control to get properties for
1775
1862
  * @return {FieldProperties} The properties associated with the control, or empty object if not found
1776
- *
1777
1863
  * @static
1778
- * @memberOf NgxDecafFormService
1779
1864
  */
1780
1865
  static getPropsFromControl(control) {
1781
1866
  return this.controls.get(control) || {};
@@ -1785,18 +1870,15 @@ class NgxDecafFormService {
1785
1870
  * @summary Traverses up the DOM tree to find the nearest parent element with the specified tag name.
1786
1871
  * This is useful for finding container elements or specific parent components in the DOM hierarchy.
1787
1872
  * The search is case-insensitive for tag name matching.
1788
- *
1789
1873
  * @param {HTMLElement} el - The starting element to traverse from
1790
1874
  * @param {string} tag - The tag name to search for (case-insensitive)
1791
1875
  * @return {HTMLElement} The found parent element with the specified tag
1792
1876
  * @throws {Error} If no parent with the specified tag is found in the DOM tree
1793
- *
1794
1877
  * @mermaid
1795
1878
  * sequenceDiagram
1796
1879
  * participant C as Component
1797
1880
  * participant NFS as NgxDecafFormService
1798
1881
  * participant DOM as DOM Tree
1799
- *
1800
1882
  * C->>NFS: getParentEl(element, tagName)
1801
1883
  * loop Traverse up DOM tree
1802
1884
  * NFS->>DOM: Get parentElement
@@ -1807,9 +1889,7 @@ class NgxDecafFormService {
1807
1889
  * NFS-->>C: Throw Error
1808
1890
  * end
1809
1891
  * end
1810
- *
1811
1892
  * @static
1812
- * @memberOf NgxDecafFormService
1813
1893
  */
1814
1894
  static getParentEl(el, tag) {
1815
1895
  let parent;
@@ -1826,30 +1906,35 @@ class NgxDecafFormService {
1826
1906
  * @summary Associates a form control with its component properties for later retrieval.
1827
1907
  * This enables the service to maintain metadata about controls without creating memory leaks,
1828
1908
  * as WeakMap automatically cleans up references when controls are garbage collected.
1829
- *
1830
1909
  * @param {AbstractControl} control - The control to register (FormControl, FormGroup, or FormArray)
1831
1910
  * @param {FieldProperties} props - The properties to associate with the control
1832
1911
  * @return {void}
1833
- *
1834
1912
  * @static
1835
- * @memberOf NgxDecafFormService
1836
1913
  */
1837
1914
  static register(control, props) {
1838
1915
  this.controls.set(control, props);
1839
1916
  }
1840
1917
  /**
1841
- * @description Unregisters a control.
1918
+ * @description Unregisters a control from the internal WeakMap.
1842
1919
  * @summary Removes a control and its associated properties from the internal WeakMap.
1843
- * @param {AbstractControl} control - The control to unregister.
1844
- * @return {boolean} True if the control was successfully unregistered, false otherwise.
1920
+ * This cleans up the metadata tracking for the control and frees up memory. Returns
1921
+ * true if the control was found and removed, false if it was not in the registry.
1922
+ * @param {AbstractControl} control - The control to unregister (FormControl, FormGroup, or FormArray)
1923
+ * @return {boolean} True if the control was successfully unregistered, false otherwise
1924
+ * @static
1845
1925
  */
1846
1926
  static unregister(control) {
1847
1927
  return this.controls.delete(control);
1848
1928
  }
1849
1929
  /**
1850
- * @description Resets a form group.
1851
- * @summary Recursively resets all controls in a form group, clearing values, errors, and marking them as pristine and untouched.
1852
- * @param {FormGroup} formGroup - The form group to reset.
1930
+ * @description Resets a form group or form control.
1931
+ * @summary Recursively resets all controls in a form group or a single form control, clearing values,
1932
+ * errors, and marking them as pristine and untouched. For FormControls, it sets the value to empty
1933
+ * string (except for checkbox types) and clears validation errors. For FormGroups, it recursively
1934
+ * resets all child controls.
1935
+ * @param {FormGroup | FormControl} formGroup - The form group or form control to reset
1936
+ * @return {void}
1937
+ * @static
1853
1938
  */
1854
1939
  static reset(formGroup) {
1855
1940
  if (formGroup instanceof FormControl) {
@@ -1873,32 +1958,29 @@ class NgxDecafFormService {
1873
1958
  }
1874
1959
 
1875
1960
  /**
1876
- * @module module:lib/engine/NgxRenderingEngine
1961
+ * @module lib/engine/NgxRenderingEngine
1877
1962
  * @description Angular rendering engine for Decaf model-driven UIs.
1878
1963
  * @summary Implements NgxRenderingEngine which converts model decorator metadata
1879
1964
  * into Angular components, manages component registration, and orchestrates
1880
1965
  * dynamic component creation and input mapping.
1881
- *
1882
1966
  * @link {@link NgxRenderingEngine}
1883
1967
  */
1884
1968
  /**
1885
- * @description Angular implementation of the RenderingEngine with enhanced features
1886
- * @summary This class extends the base RenderingEngine to provide Angular-specific rendering capabilities
1887
- * with additional features compared to NgxRenderingEngine. It handles the conversion of field definitions
1888
- * to Angular components, manages component registration, and provides utilities for component creation
1889
- * and input handling. This implementation uses Angular's newer component APIs.
1890
- *
1969
+ * @description Angular implementation of the RenderingEngine for Decaf components.
1970
+ * @summary This class extends the base RenderingEngine to provide Angular-specific rendering capabilities.
1971
+ * It handles the conversion of field definitions to Angular components, manages component registration,
1972
+ * and provides utilities for component creation and input handling. The engine converts model decorator
1973
+ * metadata into dynamically created Angular components with proper input binding and lifecycle management.
1891
1974
  * @template AngularFieldDefinition - Type for Angular-specific field definitions
1892
1975
  * @template AngularDynamicOutput - Type for Angular-specific component output
1893
- *
1894
1976
  * @class NgxRenderingEngine
1977
+ * @extends {RenderingEngine<AngularFieldDefinition, AngularDynamicOutput>}
1895
1978
  * @example
1896
1979
  * ```typescript
1897
1980
  * const engine = NgxRenderingEngine.get();
1898
1981
  * engine.initialize();
1899
1982
  * const output = engine.render(myModel, {}, viewContainerRef, injector, templateRef);
1900
1983
  * ```
1901
- *
1902
1984
  * @mermaid
1903
1985
  * sequenceDiagram
1904
1986
  * participant Client
@@ -1914,49 +1996,61 @@ class NgxDecafFormService {
1914
1996
  * Components-->>Engine: component constructor
1915
1997
  * Engine->>Engine: createComponent(component, inputs, metadata, vcr, injector, template)
1916
1998
  * Engine-->>Client: return AngularDynamicOutput
1999
+ * @memberOf module:lib/engine/NgxRenderingEngine
1917
2000
  */
1918
2001
  class NgxRenderingEngine extends RenderingEngine {
1919
2002
  /**
1920
- * @description Current operation context for component visibility control
2003
+ * @description Current operation context for component visibility control.
1921
2004
  * @summary Static property that stores the current operation being performed,
1922
2005
  * which is used to determine component visibility through the 'hidden' property.
1923
2006
  * Components can specify operations where they should be hidden, and this property
1924
2007
  * provides the context for those visibility checks. The value is typically extracted
1925
2008
  * from the global properties during the rendering process.
1926
- *
1927
2009
  * @private
1928
2010
  * @static
1929
2011
  * @type {string | undefined}
2012
+ * @memberOf module:lib/engine/NgxRenderingEngine
1930
2013
  */
1931
2014
  static { this._operation = undefined; }
1932
2015
  // private static _projectable: boolean = true
2016
+ /**
2017
+ * @description Parent component properties for child component inheritance.
2018
+ * @summary Static property that stores parent component properties that should be
2019
+ * inherited by child components. This is particularly used for passing page configuration
2020
+ * down to child components in multi-page forms. The property is cleared after rendering
2021
+ * to prevent property leakage between unrelated component trees.
2022
+ * @private
2023
+ * @static
2024
+ * @type {KeyValue | undefined}
2025
+ * @memberOf module:lib/engine/NgxRenderingEngine
2026
+ */
1933
2027
  static { this._parentProps = undefined; }
1934
2028
  /**
1935
- * @description Constructs a new NgxRenderingEngine instance
2029
+ * @description Constructs a new NgxRenderingEngine instance.
1936
2030
  * @summary Initializes a new instance of the Angular rendering engine by calling the parent
1937
2031
  * constructor with the 'angular' engine type identifier. This constructor sets up the base
1938
2032
  * rendering engine functionality with Angular-specific configurations and prepares the
1939
2033
  * instance for component registration and rendering operations.
1940
- *
1941
2034
  * @constructor
2035
+ * @memberOf module:lib/engine/NgxRenderingEngine
1942
2036
  */
1943
2037
  constructor() {
1944
- super('angular');
2038
+ super(AngularEngineKeys.FLAVOUR);
1945
2039
  }
1946
2040
  /**
1947
- * @description Converts a field definition to an Angular component output
2041
+ * @description Converts a field definition to an Angular component output.
1948
2042
  * @summary This private method takes a field definition and creates the corresponding Angular component.
1949
- * It handles component instantiation, input property mapping, and child component rendering.
1950
- * The method validates input properties against the component's metadata and processes
2043
+ * It handles component instantiation, input property mapping, child component rendering, and visibility
2044
+ * control. The method validates input properties against the component's metadata and processes
1951
2045
  * child components recursively.
1952
- *
1953
2046
  * @param {FieldDefinition<AngularFieldDefinition>} fieldDef - The field definition to convert
1954
2047
  * @param {ViewContainerRef} vcr - The view container reference for component creation
1955
2048
  * @param {Injector} injector - The Angular injector for dependency injection
1956
2049
  * @param {TemplateRef<any>} tpl - The template reference for content projection
1957
2050
  * @param {string} registryFormId - Form identifier for the component renderer
2051
+ * @param {boolean} createComponent - Whether to create the component instance
2052
+ * @param {FormParent} [formGroup] - Optional form group for form components
1958
2053
  * @return {AngularDynamicOutput} The Angular component output with component reference and inputs
1959
- *
1960
2054
  * @mermaid
1961
2055
  * sequenceDiagram
1962
2056
  * participant Method as fromFieldDefinition
@@ -1977,6 +2071,8 @@ class NgxRenderingEngine extends RenderingEngine {
1977
2071
  * Method->>Method: Create component instance
1978
2072
  * end
1979
2073
  * Method-->>Caller: return AngularDynamicOutput
2074
+ * @private
2075
+ * @memberOf module:lib/engine/NgxRenderingEngine
1980
2076
  */
1981
2077
  fromFieldDefinition(fieldDef, vcr, injector, tpl, registryFormId = Date.now().toString(36).toUpperCase(), createComponent = true, formGroup) {
1982
2078
  const cmp = fieldDef?.['component'] || NgxRenderingEngine.components(fieldDef.tag);
@@ -2046,11 +2142,10 @@ class NgxRenderingEngine extends RenderingEngine {
2046
2142
  return result;
2047
2143
  }
2048
2144
  /**
2049
- * @description Creates an Angular component instance
2145
+ * @description Creates an Angular component instance with inputs and template projection.
2050
2146
  * @summary This static utility method creates an Angular component instance with the specified
2051
2147
  * inputs and template. It uses Angular's component creation API to instantiate the component
2052
2148
  * and then sets the input properties using the provided metadata.
2053
- *
2054
2149
  * @param {Type<unknown>} component - The component type to create
2055
2150
  * @param {KeyValue} [inputs={}] - The input properties to set on the component
2056
2151
  * @param {ComponentMirror<unknown>} metadata - The component metadata for input validation
@@ -2058,6 +2153,8 @@ class NgxRenderingEngine extends RenderingEngine {
2058
2153
  * @param {Injector} injector - The Angular injector for dependency injection
2059
2154
  * @param {Node[]} [template=[]] - The template nodes to project into the component
2060
2155
  * @return {ComponentRef<unknown>} The created component reference
2156
+ * @static
2157
+ * @memberOf module:lib/engine/NgxRenderingEngine
2061
2158
  */
2062
2159
  static createComponent(component, inputs = {}, metadata, vcr, injector, template = []) {
2063
2160
  const componentInstance = vcr.createComponent(component, {
@@ -2068,26 +2165,29 @@ class NgxRenderingEngine extends RenderingEngine {
2068
2165
  return componentInstance;
2069
2166
  }
2070
2167
  /**
2071
- * @description Extracts decorator metadata from a model
2168
+ * @description Extracts decorator metadata from a model.
2072
2169
  * @summary This method provides access to the field definition generated from a model's
2073
2170
  * decorators. It's a convenience wrapper around the toFieldDefinition method that
2074
2171
  * converts a model to a field definition based on its decorators and the provided
2075
2172
  * global properties.
2076
- *
2077
2173
  * @param {Model} model - The model to extract decorators from
2078
2174
  * @param {Record<string, unknown>} globalProps - Global properties to include in the field definition
2079
2175
  * @return {FieldDefinition<AngularFieldDefinition>} The field definition generated from the model
2176
+ * @memberOf module:lib/engine/NgxRenderingEngine
2080
2177
  */
2081
2178
  getDecorators(model, globalProps) {
2082
2179
  return this.toFieldDefinition(model, globalProps);
2083
2180
  }
2084
2181
  /**
2085
- * @description Destroys the current engine instance
2086
- * @summary This static method clears the current instance reference, effectively
2087
- * destroying the singleton instance of the rendering engine. This can be used
2088
- * to reset the engine state or to prepare for a new instance creation.
2089
- *
2182
+ * @description Destroys the current engine instance and cleans up resources.
2183
+ * @summary This static method clears the current instance reference and parent props,
2184
+ * effectively destroying the singleton instance of the rendering engine. Optionally
2185
+ * removes the form registry for the specified form ID. This can be used to reset the
2186
+ * engine state or to prepare for a new instance creation.
2187
+ * @param {string} [formId] - Optional form ID to remove from registry
2090
2188
  * @return {Promise<void>} A promise that resolves when the instance is destroyed
2189
+ * @static
2190
+ * @memberOf module:lib/engine/NgxRenderingEngine
2091
2191
  */
2092
2192
  static async destroy(formId) {
2093
2193
  if (formId)
@@ -2096,12 +2196,11 @@ class NgxRenderingEngine extends RenderingEngine {
2096
2196
  NgxRenderingEngine._parentProps = undefined;
2097
2197
  }
2098
2198
  /**
2099
- * @description Renders a model into an Angular component output
2199
+ * @description Renders a model into an Angular component output.
2100
2200
  * @summary This method takes a model and converts it to an Angular component output.
2101
2201
  * It first stores a reference to the model, then converts it to a field definition
2102
2202
  * using the base RenderingEngine's toFieldDefinition method, and finally converts
2103
2203
  * that field definition to an Angular component output using fromFieldDefinition.
2104
- *
2105
2204
  * @template M - Type extending Model
2106
2205
  * @param {M} model - The model to render
2107
2206
  * @param {Record<string, unknown>} globalProps - Global properties to pass to the component
@@ -2109,7 +2208,6 @@ class NgxRenderingEngine extends RenderingEngine {
2109
2208
  * @param {Injector} injector - The Angular injector for dependency injection
2110
2209
  * @param {TemplateRef<any>} tpl - The template reference for content projection
2111
2210
  * @return {AngularDynamicOutput} The Angular component output with component reference and inputs
2112
- *
2113
2211
  * @mermaid
2114
2212
  * sequenceDiagram
2115
2213
  * participant Client as Client Code
@@ -2124,6 +2222,8 @@ class NgxRenderingEngine extends RenderingEngine {
2124
2222
  * Render->>FromField: fromFieldDefinition(fieldDef, vcr, injector, tpl)
2125
2223
  * FromField-->>Render: AngularDynamicOutput
2126
2224
  * Render-->>Client: return AngularDynamicOutput
2225
+ * @override
2226
+ * @memberOf module:lib/engine/NgxRenderingEngine
2127
2227
  */
2128
2228
  render(model, globalProps, vcr, injector, tpl) {
2129
2229
  let result;
@@ -2147,12 +2247,13 @@ class NgxRenderingEngine extends RenderingEngine {
2147
2247
  return result;
2148
2248
  }
2149
2249
  /**
2150
- * @description Initializes the rendering engine
2250
+ * @description Initializes the rendering engine.
2151
2251
  * @summary This method initializes the rendering engine. It checks if the engine is already initialized
2152
2252
  * and sets the initialized flag to true. This method is called before the engine is used
2153
2253
  * to ensure it's properly set up for rendering operations.
2154
- *
2155
2254
  * @return {Promise<void>} A promise that resolves when initialization is complete
2255
+ * @override
2256
+ * @memberOf module:lib/engine/NgxRenderingEngine
2156
2257
  */
2157
2258
  async initialize() {
2158
2259
  if (this.initialized)
@@ -2161,15 +2262,16 @@ class NgxRenderingEngine extends RenderingEngine {
2161
2262
  this.initialized = true;
2162
2263
  }
2163
2264
  /**
2164
- * @description Registers a component with the rendering engine
2265
+ * @description Registers a component with the rendering engine.
2165
2266
  * @summary This static method registers a component constructor with the rendering engine
2166
2267
  * under a specific name. It initializes the components registry if needed and throws
2167
2268
  * an error if a component is already registered under the same name to prevent
2168
2269
  * accidental overrides.
2169
- *
2170
2270
  * @param {string} name - The name to register the component under
2171
2271
  * @param {Constructor<unknown>} constructor - The component constructor
2172
2272
  * @return {void}
2273
+ * @static
2274
+ * @memberOf module:lib/engine/NgxRenderingEngine
2173
2275
  */
2174
2276
  static registerComponent(name, constructor) {
2175
2277
  if (!this._components)
@@ -2181,14 +2283,15 @@ class NgxRenderingEngine extends RenderingEngine {
2181
2283
  };
2182
2284
  }
2183
2285
  /**
2184
- * @description Retrieves registered components from the rendering engine
2286
+ * @description Retrieves registered components from the rendering engine.
2185
2287
  * @summary This static method retrieves either all registered components or a specific component
2186
2288
  * by its selector. When called without a selector, it returns an array of all registered
2187
2289
  * components. When called with a selector, it returns the specific component if found,
2188
2290
  * or throws an error if the component is not registered.
2189
- *
2190
2291
  * @param {string} [selector] - Optional selector to retrieve a specific component
2191
2292
  * @return {Object|Array} Either a specific component or an array of all components
2293
+ * @static
2294
+ * @memberOf module:lib/engine/NgxRenderingEngine
2192
2295
  */
2193
2296
  static components(selector) {
2194
2297
  if (!selector)
@@ -2198,30 +2301,30 @@ class NgxRenderingEngine extends RenderingEngine {
2198
2301
  return this._components[selector];
2199
2302
  }
2200
2303
  /**
2201
- * @description Generates a key for reflection metadata
2304
+ * @description Generates a key for reflection metadata storage.
2202
2305
  * @summary This static method generates a key for reflection metadata by prefixing the input key
2203
2306
  * with the Angular engine's reflection prefix. This is used for storing and retrieving
2204
2307
  * metadata in a namespaced way to avoid conflicts with other metadata.
2205
- *
2206
2308
  * @param {string} key - The base key to prefix
2207
2309
  * @return {string} The prefixed key for reflection metadata
2310
+ * @static
2311
+ * @override
2312
+ * @memberOf module:lib/engine/NgxRenderingEngine
2208
2313
  */
2209
2314
  static key(key) {
2210
2315
  return `${AngularEngineKeys.REFLECT}${key}`;
2211
2316
  }
2212
2317
  /**
2213
- * @description Sets input properties on a component instance
2318
+ * @description Sets input properties on a component instance.
2214
2319
  * @summary This static utility method sets input properties on a component instance
2215
2320
  * based on the provided inputs object and component metadata. It handles both simple
2216
2321
  * values and nested objects, recursively processing object properties. The method
2217
2322
  * validates each input against the component's metadata to ensure only valid inputs
2218
2323
  * are set.
2219
- *
2220
2324
  * @param {ComponentRef<unknown>} component - The component reference to set inputs on
2221
2325
  * @param {KeyValue} inputs - The input properties to set
2222
2326
  * @param {ComponentMirror<unknown>} metadata - The component metadata for input validation
2223
2327
  * @return {void}
2224
- *
2225
2328
  * @mermaid
2226
2329
  * sequenceDiagram
2227
2330
  * participant Caller
@@ -2241,6 +2344,8 @@ class NgxRenderingEngine extends RenderingEngine {
2241
2344
  * SetInputs->>Component: setInput(key, value)
2242
2345
  * end
2243
2346
  * end
2347
+ * @static
2348
+ * @memberOf module:lib/engine/NgxRenderingEngine
2244
2349
  */
2245
2350
  static setInputs(component, inputs, metadata) {
2246
2351
  function parseInputValue(component, input) {
@@ -2343,7 +2448,8 @@ var errors = {
2343
2448
  lessThanOrEqual: "This field must be less than or equal to field {0}",
2344
2449
  greaterThan: "This field must be greater than field {0}",
2345
2450
  greaterThanOrEqual: "This field must be greater than or equal to field {0}",
2346
- form_control: "Controlador do formulário não encontrado para {0}."
2451
+ form_control: "Controlador do formulário não encontrado para {0}.",
2452
+ not_found: "No records found with <span class=\"text-bold\">{0}: {1} in {2}</span>."
2347
2453
  };
2348
2454
  var component = {
2349
2455
  fieldset: {
@@ -2377,7 +2483,7 @@ var component = {
2377
2483
  },
2378
2484
  empty_state: {
2379
2485
  title: "No data found",
2380
- button: "Create",
2486
+ button_create: "Create",
2381
2487
  search: {
2382
2488
  title: "Not data found for search",
2383
2489
  subtitle: "You searched for: <ion-text color=\"primary\" class=\"display-block ion-text-center text-bold\">{0}</ion-text>",
@@ -2506,12 +2612,11 @@ function provideI18n(config = { fallbackLang: 'en', lang: 'en' }, resources = []
2506
2612
  }
2507
2613
 
2508
2614
  /**
2509
- * @module module:lib/engine/NgxDecafComponentDirective
2615
+ * @module lib/engine/NgxDecafComponentDirective
2510
2616
  * @description Base decaf component abstraction providing shared inputs and utilities.
2511
2617
  * @summary NgxDecafComponentDirective is the abstract foundation for Decaf components and provides common
2512
2618
  * inputs (model, mapper, pk, props), logging, repository resolution, and event dispatch helpers.
2513
2619
  * It centralizes shared behavior for child components and simplifies integration with the rendering engine.
2514
- *
2515
2620
  * @link {@link NgxDecafComponentDirective}
2516
2621
  */
2517
2622
  try {
@@ -2520,194 +2625,250 @@ try {
2520
2625
  catch (e) {
2521
2626
  throw new Error(`Failed to load rendering engine: ${e}`);
2522
2627
  }
2628
+ /**
2629
+ * @description Base directive for Decaf components in Angular applications.
2630
+ * @summary Abstract base class that provides common functionality for all Decaf components.
2631
+ * This directive establishes a foundation for component development by offering shared inputs
2632
+ * (model, mapper, pk, props), logging infrastructure, repository access, event handling, and
2633
+ * internationalization support. It implements OnChanges to respond to input property changes
2634
+ * and includes utilities for navigation, localization, and dynamic property binding. All Decaf
2635
+ * components should extend this directive to inherit its foundational capabilities.
2636
+ * @class NgxDecafComponentDirective
2637
+ * @extends {LoggedClass}
2638
+ * @implements {OnChanges}
2639
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2640
+ */
2523
2641
  class NgxDecafComponentDirective extends LoggedClass {
2642
+ /**
2643
+ * @description Constructor for NgxDecafComponentDirective.
2644
+ * @summary Initializes the directive by setting up the component name, locale root,
2645
+ * and logger. Calls the parent LoggedClass constructor and configures localization
2646
+ * context. The component name and locale root can be optionally injected via the
2647
+ * CPTKN token, otherwise defaults are used.
2648
+ * @param {string} [componentName] - Optional component name for identification and logging
2649
+ * @param {string} [localeRoot] - Optional locale root key for internationalization
2650
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2651
+ */
2524
2652
  // eslint-disable-next-line @angular-eslint/prefer-inject
2525
2653
  constructor(componentName, localeRoot) {
2526
2654
  super();
2527
- this.componentName = componentName;
2528
- this.localeRoot = localeRoot;
2529
2655
  /**
2530
- * @description Unique identifier for the current record.
2531
- * @summary A unique identifier for the current record being displayed or manipulated.
2532
- * This is typically used in conjunction with the primary key for operations on specific records.
2533
- *
2656
+ * @description Unique identifier for the component instance.
2657
+ * @summary A unique identifier automatically generated for each component instance.
2658
+ * This UID is used for DOM element identification, component tracking, and debugging purposes.
2659
+ * By default, it generates a random 16-character value, but it can be explicitly set via input.
2534
2660
  * @type {string | number}
2535
- * @memberOf NgxDecafComponentDirective
2661
+ * @default generateRandomValue(16)
2662
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2536
2663
  */
2537
2664
  this.uid = generateRandomValue(16);
2538
2665
  /**
2539
- * @description Field mapping configuration.
2666
+ * @description Field mapping configuration object or function.
2540
2667
  * @summary Defines how fields from the data model should be mapped to properties used by the component.
2541
- * This allows for flexible data binding between the model and the component's display logic.
2542
- *
2543
- * @type {Record<string, string>}
2544
- * @memberOf NgxDecafComponentDirective
2668
+ * This allows for flexible data binding between the model and the component's display logic. Can be
2669
+ * provided as a static object mapping or as a function for dynamic mapping transformations.
2670
+ * @type {Record<string, string> | FunctionLike}
2671
+ * @default {}
2672
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2545
2673
  */
2546
2674
  this.mapper = {};
2547
2675
  /**
2548
- * @description Available CRUD operations for this component.
2676
+ * @description Available CRUD operations for this component instance.
2549
2677
  * @summary Defines which CRUD operations (Create, Read, Update, Delete) are available
2550
- * for this component. This affects which operations can be performed on the data.
2551
- *
2678
+ * for this component. This affects which operations can be performed on the data and
2679
+ * which operation buttons are displayed in the UI. By default, only READ operations are enabled.
2680
+ * @type {CrudOperations[]}
2552
2681
  * @default [OperationKeys.READ]
2553
- * @memberOf NgxDecafComponentDirective
2682
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2554
2683
  */
2555
2684
  this.operations = [OperationKeys.READ];
2556
2685
  /**
2557
- * @description Primary key field name for the model.
2558
- * @summary Specifies which field in the model should be used as the primary key.
2559
- * This is typically used for identifying unique records in operations like update and delete.
2686
+ * @description The CRUD operation type to be performed on the model.
2687
+ * @summary Specifies which operation (Create, Read, Update, Delete) this component instance
2688
+ * should perform. This determines the UI behavior, form configuration, and available actions.
2689
+ * The operation affects form validation, field availability, and the specific repository
2690
+ * method called during data submission.
2560
2691
  *
2692
+ * @type {OperationKeys.CREATE | OperationKeys.READ | OperationKeys.UPDATE | OperationKeys.DELETE}
2693
+ * @default OperationKeys.READ
2694
+ * @memberOf ModelPage
2695
+ */
2696
+ this.operation = OperationKeys.READ;
2697
+ /**
2698
+ * @description Row position in a grid-based layout system.
2699
+ * @summary Specifies the row position of this component when rendered within a grid-based layout.
2700
+ * This property is used for positioning components in multi-row, multi-column layouts and helps
2701
+ * establish the component's vertical placement within the grid structure.
2561
2702
  * @type {number}
2562
2703
  * @default 1
2563
- * @memberOf NgxDecafComponentDirective
2704
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2564
2705
  */
2565
2706
  this.row = 1;
2566
2707
  /**
2567
- * @description Primary key field name for the model.
2568
- * @summary Specifies which field in the model should be used as the primary key.
2569
- * This is typically used for identifying unique records in operations like update and delete.
2570
- *
2708
+ * @description Column position in a grid-based layout system.
2709
+ * @summary Specifies the column position of this component when rendered within a grid-based layout.
2710
+ * This property is used for positioning components in multi-row, multi-column layouts and helps
2711
+ * establish the component's horizontal placement within the grid structure.
2571
2712
  * @type {number}
2572
2713
  * @default 1
2573
- * @memberOf NgxDecafComponentDirective
2714
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2574
2715
  */
2575
2716
  this.col = 1;
2576
2717
  /**
2577
- * @description Additional CSS class names to apply to the component.
2718
+ * @description Additional CSS class names for component styling.
2578
2719
  * @summary Allows custom CSS classes to be added to the component's root element.
2579
2720
  * These classes are appended to any automatically generated classes based on other
2580
2721
  * component properties. Multiple classes can be provided as a space-separated string.
2581
2722
  * This provides a way to customize the component's appearance beyond the built-in styling options.
2582
- *
2583
2723
  * @type {string}
2584
2724
  * @default ""
2585
- * @memberOf NgxDecafComponentDirective
2725
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2586
2726
  */
2587
2727
  this.className = '';
2588
2728
  /**
2589
- * @description Root component of the Decaf-ts for Angular application
2590
- * @summary This component serves as the main entry point for the application.
2591
- * It sets up the navigation menu, handles routing events, and initializes
2592
- * the application state. It also manages the application title and menu visibility.
2593
- *
2594
- * @private
2729
+ * @description Ionic menu controller service for menu management.
2730
+ * @summary Injected service that provides programmatic control over Ionic menu components.
2731
+ * This service allows the component to open, close, toggle, and manage menu states within
2732
+ * the application. It provides access to menu functionality for implementing navigation
2733
+ * and layout features that require menu interaction.
2734
+ * @protected
2595
2735
  * @type {MenuController}
2596
- * @memberOf NgxDecafComponentDirective
2736
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2597
2737
  */
2598
2738
  this.menuController = inject(MenuController);
2599
2739
  /**
2600
- * @description Angular change detection service.
2601
- * @summary Injected service that provides manual control over change detection cycles.
2602
- * This is essential for ensuring that programmatic DOM changes (like setting accordion
2603
- * attributes) are properly reflected in the component's state and trigger appropriate
2604
- * view updates when modifications occur outside the normal Angular change detection flow.
2605
- *
2606
- * @protected
2607
- * @type {ChangeDetectorRef}
2608
- * @memberOf CrudFormComponent
2609
- */
2740
+ * @description Angular change detection service for manual change detection control.
2741
+ * @summary Injected service that provides manual control over change detection cycles.
2742
+ * This is essential for ensuring that programmatic DOM changes (like setting accordion
2743
+ * attributes) are properly reflected in the component's state and trigger appropriate
2744
+ * view updates when modifications occur outside the normal Angular change detection flow.
2745
+ * @protected
2746
+ * @type {ChangeDetectorRef}
2747
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2748
+ */
2610
2749
  this.changeDetectorRef = inject(ChangeDetectorRef);
2611
2750
  /**
2612
- * @description Angular Renderer2 service for safe DOM manipulation.
2751
+ * @description Angular Renderer2 service for platform-agnostic DOM manipulation.
2613
2752
  * @summary Injected service that provides a safe, platform-agnostic way to manipulate DOM elements.
2614
2753
  * This service ensures proper handling of DOM operations across different platforms and environments,
2615
- * including server-side rendering and web workers.
2616
- *
2754
+ * including server-side rendering and web workers, without direct DOM access.
2617
2755
  * @protected
2618
2756
  * @type {Renderer2}
2619
- * @memberOf CrudFormComponent
2757
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2620
2758
  */
2621
2759
  this.renderer = inject(Renderer2);
2622
2760
  /**
2623
- * @description Translation service for internationalization.
2761
+ * @description Translation service for application internationalization.
2624
2762
  * @summary Injected service that provides translation capabilities for UI text.
2625
- * Used to translate button labels and validation messages based on the current locale.
2626
- *
2763
+ * Used to translate button labels, validation messages, and other text content based
2764
+ * on the current locale setting, enabling multilingual support throughout the application.
2627
2765
  * @protected
2628
2766
  * @type {TranslateService}
2629
- * @memberOf CrudFormComponent
2767
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2630
2768
  */
2631
2769
  this.translateService = inject(TranslateService);
2632
2770
  /**
2633
- * @description Event emitter for custom renderer events.
2634
- * @summary Emits custom events that occur within child components or the layout itself.
2771
+ * @description Event emitter for custom component events.
2772
+ * @summary Emits custom events that occur within child components or the component itself.
2635
2773
  * This allows parent components to listen for and respond to user interactions or
2636
- * state changes within the grid layout. Events are passed up the component hierarchy
2637
- * to enable coordinated behavior across the application.
2638
- *
2774
+ * state changes. Events are passed up the component hierarchy to enable coordinated
2775
+ * behavior across the application.
2639
2776
  * @type {EventEmitter<IBaseCustomEvent>}
2640
- * @memberOf NgxDecafComponentDirective
2777
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2641
2778
  */
2642
2779
  this.listenEvent = new EventEmitter();
2643
2780
  /**
2644
- * @description Angular Router instance for navigation
2645
- * @summary Injected Router service used for programmatic navigation
2646
- * to other pages after successful login or other routing operations.
2647
- *
2648
- * @private
2781
+ * @description Angular Router instance for programmatic navigation.
2782
+ * @summary Injected Router service used for programmatic navigation between routes
2783
+ * in the application. This service enables navigation to different views and operations,
2784
+ * handles route parameters, and manages the browser's navigation history.
2785
+ * @protected
2649
2786
  * @type {Router}
2650
- * @memberOf NgxDecafComponentDirective
2787
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2651
2788
  */
2652
2789
  this.router = inject(Router);
2653
2790
  /**
2654
- * @description Configuration for list item rendering
2791
+ * @description Configuration for list item rendering behavior.
2655
2792
  * @summary Defines how list items should be rendered in the component.
2656
2793
  * This property holds a configuration object that specifies the tag name
2657
2794
  * and other properties needed to render list items correctly. The tag property
2658
2795
  * identifies which component should be used to render each item in a list.
2659
2796
  * Additional properties can be included to customize the rendering behavior.
2660
- *
2661
2797
  * @type {Record<string, unknown>}
2662
2798
  * @default {tag: ""}
2663
- * @memberOf NgxDecafComponentDirective
2799
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2664
2800
  */
2665
2801
  this.item = { tag: '' };
2666
2802
  /**
2667
- * @description Dynamic properties configuration object.
2803
+ * @description Dynamic properties configuration for runtime customization.
2668
2804
  * @summary Contains key-value pairs of dynamic properties that can be applied to the component
2669
2805
  * at runtime. This flexible configuration object allows for dynamic property assignment without
2670
2806
  * requiring explicit input bindings for every possible configuration option. Properties from
2671
2807
  * this object are parsed and applied to the component instance through the parseProps method,
2672
2808
  * enabling customizable component behavior based on external configuration.
2673
- *
2674
2809
  * @type {Record<string, unknown>}
2675
2810
  * @default {}
2676
- * @memberOf NgxDecafComponentDirective
2811
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2677
2812
  */
2678
2813
  this.props = {};
2679
2814
  /**
2680
- * @description Flag indicating if the component has been initialized
2815
+ * @description Initialization status flag for the component.
2681
2816
  * @summary Tracks whether the component has completed its initialization process.
2682
2817
  * This flag is used to prevent duplicate initialization and to determine if
2683
2818
  * certain operations that require initialization can be performed.
2684
- *
2685
2819
  * @type {boolean}
2686
2820
  * @default false
2687
- * @memberOf NgxDecafComponentDirective
2821
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2688
2822
  */
2689
2823
  this.initialized = false;
2690
- this.componentName = this.componentName || "NgxDecafComponentDirective";
2691
- if (!this.localeRoot)
2824
+ /**
2825
+ * @description Reference to CRUD operation constants for template usage.
2826
+ * @summary Exposes the OperationKeys enum to the component template, enabling
2827
+ * conditional rendering and behavior based on operation types. This protected
2828
+ * readonly property ensures that template logic can access operation constants
2829
+ * while maintaining encapsulation and preventing accidental modification.
2830
+ * @protected
2831
+ * @readonly
2832
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2833
+ */
2834
+ this.OperationKeys = OperationKeys;
2835
+ /**
2836
+ * @description Angular Location service.
2837
+ * @summary Injected service that provides access to the browser's URL and history.
2838
+ * This service is used for interacting with the browser's history API, allowing
2839
+ * for back navigation and URL manipulation outside of Angular's router.
2840
+ *
2841
+ * @private
2842
+ * @type {Location}
2843
+ */
2844
+ this.location = inject(Location);
2845
+ this.componentName = componentName || "NgxDecafComponentDirective";
2846
+ this.localeRoot = localeRoot;
2847
+ if (!this.localeRoot && this.componentName)
2692
2848
  this.localeRoot = this.componentName;
2849
+ if (this.localeRoot)
2850
+ this.getLocale(this.localeRoot);
2693
2851
  this.logger = this.log;
2694
2852
  }
2853
+ /**
2854
+ * @description Getter for the current locale context identifier.
2855
+ * @summary Returns the current locale identifier by calling the getLocale method.
2856
+ * This property provides convenient access to the component's active locale setting.
2857
+ * @returns {string} The current locale identifier
2858
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2859
+ */
2695
2860
  get localeContext() {
2696
2861
  return this.getLocale();
2697
2862
  }
2698
2863
  /**
2699
- * @description Getter for the repository instance.
2700
- * @summary Provides a connection to the data layer for retrieving and manipulating data.
2701
- * This method initializes the `_repository` property if it is not already set, ensuring
2702
- * that a single instance of the repository is used throughout the component.
2703
- *
2704
- * The repository is used to perform CRUD operations on the data model, such as fetching data,
2705
- * creating new items, updating existing items, and deleting items. It also provides methods
2706
- * for querying and filtering data based on specific criteria.
2707
- *
2708
- * @returns {DecafRepository<Model>} The initialized repository instance.
2709
- * @private
2710
- * @memberOf NgxDecafComponentDirective
2864
+ * @description Getter for the data repository instance.
2865
+ * @summary Lazily initializes and returns the DecafRepository instance for the current model.
2866
+ * This getter ensures the repository is created only when needed and reused for subsequent
2867
+ * access. It also automatically sets the primary key field if not explicitly configured.
2868
+ * @protected
2869
+ * @returns {DecafRepository<Model>} The repository instance for the current model
2870
+ * @throws {InternalError} If repository initialization fails
2871
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2711
2872
  */
2712
2873
  get repository() {
2713
2874
  try {
@@ -2724,16 +2885,14 @@ class NgxDecafComponentDirective extends LoggedClass {
2724
2885
  return this._repository;
2725
2886
  }
2726
2887
  /**
2727
- * @description Handles changes to component inputs
2728
- * @summary This Angular lifecycle hook is called when input properties change.
2729
- * It responds to changes in the model, locale, or translatable properties by
2730
- * updating the component's internal state accordingly. When the model changes,
2731
- * it calls getModel to process the new model and getLocale to update the locale.
2732
- * When locale or translatable properties change, it calls getLocale to update
2733
- * the translation settings.
2734
- *
2735
- * @param {SimpleChanges} changes - Object containing changed properties
2888
+ * @description Angular lifecycle hook for handling input property changes.
2889
+ * @summary Responds to changes in component input properties, specifically monitoring changes
2890
+ * to the model, locale root, and component name properties. When the model changes, it triggers
2891
+ * model initialization and locale context updates. When locale-related properties change,
2892
+ * it updates the component's locale setting accordingly.
2893
+ * @param {SimpleChanges} changes - Object containing the changed properties with their previous and current values
2736
2894
  * @return {void}
2895
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2737
2896
  */
2738
2897
  ngOnChanges(changes) {
2739
2898
  if (changes[BaseComponentProps.MODEL]) {
@@ -2745,6 +2904,18 @@ class NgxDecafComponentDirective extends LoggedClass {
2745
2904
  if (changes[BaseComponentProps.LOCALE_ROOT] || changes[BaseComponentProps.COMPONENT_NAME])
2746
2905
  this.locale = this.localeContext;
2747
2906
  }
2907
+ /**
2908
+ * @description Translates text phrases using the translation service.
2909
+ * @summary Provides a promise-based wrapper around the translation service to translate
2910
+ * UI text based on the current locale. Supports both single phrases and arrays of phrases,
2911
+ * and accepts optional parameters for template interpolation. When a string parameter is
2912
+ * provided, it's automatically converted to an object format for the translation service.
2913
+ * @protected
2914
+ * @param {string | string[]} phrase - The translation key or array of keys to translate
2915
+ * @param {object | string} [params] - Optional parameters for interpolation in translated text
2916
+ * @return {Promise<string>} A promise that resolves to the translated text
2917
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2918
+ */
2748
2919
  translate(phrase, params) {
2749
2920
  return new Promise((resolve) => {
2750
2921
  if (typeof params === Primitives.STRING)
@@ -2752,10 +2923,31 @@ class NgxDecafComponentDirective extends LoggedClass {
2752
2923
  return resolve(firstValueFrom(this.translateService.get(phrase, (params || {}))));
2753
2924
  });
2754
2925
  }
2926
+ /**
2927
+ * @description Initializes the component asynchronously with custom logic.
2928
+ * @summary Abstract initialization method that can be overridden by child components to perform
2929
+ * custom initialization logic. By default, it simply sets the initialized flag to true.
2930
+ * Child components can extend this method to load data, configure settings, or perform
2931
+ * other setup operations required before the component is fully functional.
2932
+ * @protected
2933
+ * @param {...unknown[]} args - Variable number of arguments that can be used by child implementations
2934
+ * @return {Promise<void>} A promise that resolves when initialization is complete
2935
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2936
+ */
2755
2937
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2756
2938
  async initialize(...args) {
2757
2939
  this.initialized = true;
2758
2940
  }
2941
+ /**
2942
+ * @description Retrieves or sets the locale context for the component.
2943
+ * @summary Gets the locale identifier from the locale context system. If a locale parameter
2944
+ * is provided, it updates the localeRoot property and resolves the new locale context.
2945
+ * If no locale is currently set, it initializes it from the localeRoot.
2946
+ * @protected
2947
+ * @param {string} [locale] - Optional locale identifier to set
2948
+ * @return {string} The current locale identifier
2949
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2950
+ */
2759
2951
  getLocale(locale) {
2760
2952
  if (locale || !this.locale) {
2761
2953
  if (locale)
@@ -2765,13 +2957,13 @@ class NgxDecafComponentDirective extends LoggedClass {
2765
2957
  return this.locale;
2766
2958
  }
2767
2959
  /**
2768
- * @description Gets the route for the component
2769
- * @summary Retrieves the route path for the component, generating one based on the model
2770
- * if no route is explicitly set. This method checks if a route is already defined, and if not,
2771
- * it creates a default route based on the model's constructor name. The generated route follows
2772
- * the pattern '/model/{ModelName}'. This is useful for automatic routing in CRUD operations.
2773
- *
2774
- * @return {string} The route path for the component, or empty string if no route is available
2960
+ * @description Retrieves or generates the route path for the component.
2961
+ * @summary Gets the navigation route associated with this component. If no route is explicitly
2962
+ * set and a model is available, it automatically generates a route based on the model's
2963
+ * class name using the pattern `/model/{ModelName}`. Returns an empty string if neither
2964
+ * a route nor a model is available.
2965
+ * @return {string} The route path for this component
2966
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2775
2967
  */
2776
2968
  getRoute() {
2777
2969
  if (!this.route && this.model instanceof Model)
@@ -2779,14 +2971,15 @@ class NgxDecafComponentDirective extends LoggedClass {
2779
2971
  return this.route || '';
2780
2972
  }
2781
2973
  /**
2782
- * @description Resolves and sets the component's model
2783
- * @summary Processes the provided model parameter, which can be either a Model instance
2784
- * or a string identifier. If a string is provided, it attempts to resolve the actual model
2785
- * from the injectables registry. After resolving the model, it calls setModelDefinitions
2786
- * to configure the component based on the model's metadata.
2787
- *
2788
- * @param {string | Model} model - The model instance or identifier string
2974
+ * @description Resolves and initializes a model from various input formats.
2975
+ * @summary Accepts a model in multiple formats (string name, Model instance, or ModelConstructor)
2976
+ * and resolves it to a Model instance. When a string is provided, it looks up the model
2977
+ * by name in the Model registry. After resolution, it delegates to setModelDefinitions
2978
+ * to complete the model initialization and configuration.
2979
+ * @template M - The model type extending from Model
2980
+ * @param {string | Model | ModelConstructor<M>} model - The model to resolve and initialize
2789
2981
  * @return {void}
2982
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2790
2983
  */
2791
2984
  getModel(model) {
2792
2985
  if (!(model instanceof Model) && typeof model === Primitives.STRING)
@@ -2794,16 +2987,16 @@ class NgxDecafComponentDirective extends LoggedClass {
2794
2987
  this.setModelDefinitions(this.model);
2795
2988
  }
2796
2989
  /**
2797
- * @description Configures component properties based on model metadata
2798
- * @summary Extracts and applies configuration from the model's decorators to set up
2799
- * the component. This method uses the rendering engine to retrieve decorator metadata
2800
- * from the model, then configures the component's mapper and item properties accordingly.
2801
- * It ensures the route is properly set and merges various properties from the model's
2802
- * metadata into the component's configuration.
2803
- *
2804
- * @param {Model} model - The model to extract configuration from
2805
- * @return {void}
2806
- */
2990
+ * @description Configures component properties based on model decorators and metadata.
2991
+ * @summary Extracts rendering configuration from the model's decorators using the rendering
2992
+ * engine. This includes props, item configuration, and child component definitions. It sets
2993
+ * up the mapper for field transformations, configures the item renderer with appropriate
2994
+ * properties, and establishes the route for navigation. This method bridges the model's
2995
+ * decorator metadata with the component's runtime configuration.
2996
+ * @param {Model} model - The model instance to extract definitions from
2997
+ * @return {void}
2998
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2999
+ */
2807
3000
  setModelDefinitions(model) {
2808
3001
  if (model instanceof Model) {
2809
3002
  this.getRoute();
@@ -2829,33 +3022,34 @@ class NgxDecafComponentDirective extends LoggedClass {
2829
3022
  * component instance. This allows for dynamic property assignment based on configuration
2830
3023
  * stored in the props object, enabling flexible component customization without requiring
2831
3024
  * explicit property binding for every possible configuration option.
2832
- *
2833
3025
  * The method performs a safe property assignment by checking if each key from the instance
2834
3026
  * exists in the props object before applying it. This prevents accidental property
2835
3027
  * overwriting and ensures only intended properties are modified.
2836
- *
2837
3028
  * @param {KeyValue} instance - The component instance object to process
3029
+ * @param {string[]} [skip=[]] - Array of property names to skip during parsing
2838
3030
  * @return {void}
2839
- *
2840
3031
  * @mermaid
2841
3032
  * sequenceDiagram
2842
3033
  * participant C as Component
2843
- * participant B as NgxBaseComponentDirective
3034
+ * participant D as NgxDecafComponentDirective
2844
3035
  * participant P as Props Object
2845
3036
  *
2846
- * C->>B: parseProps(instance)
2847
- * B->>B: Get Object.keys(instance)
3037
+ * C->>D: parseProps(instance, skip)
3038
+ * D->>D: Get Object.keys(instance)
2848
3039
  * loop For each key in instance
2849
- * B->>P: Check if key exists in this.props
2850
- * alt Key exists in props
2851
- * B->>B: Set this[key] = this.props[key]
2852
- * else Key not in props
2853
- * Note over B: Skip this key
3040
+ * D->>D: Check if key in skip array
3041
+ * alt Key not in skip
3042
+ * D->>P: Check if key exists in this.props
3043
+ * alt Key exists in props
3044
+ * D->>D: Set this[key] = this.props[key]
3045
+ * D->>P: delete this.props[key]
3046
+ * else Key not in props
3047
+ * Note over D: Skip this key
3048
+ * end
2854
3049
  * end
2855
3050
  * end
2856
- *
2857
3051
  * @protected
2858
- * @memberOf NgxBaseComponentDirective
3052
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2859
3053
  */
2860
3054
  parseProps(instance, skip = []) {
2861
3055
  Object.keys(instance).forEach((key) => {
@@ -2866,21 +3060,55 @@ class NgxDecafComponentDirective extends LoggedClass {
2866
3060
  });
2867
3061
  }
2868
3062
  /**
2869
- * @description Tracks items in ngFor loops for optimal change detection.
3063
+ * @description Tracks items in ngFor loops for optimal change detection performance.
2870
3064
  * @summary Provides a tracking function for Angular's *ngFor directive to optimize rendering
2871
3065
  * performance. This method generates unique identifiers for list items based on their index
2872
3066
  * and content, allowing Angular to efficiently track changes and minimize DOM manipulations
2873
3067
  * during list updates. The tracking function is essential for maintaining component state
2874
3068
  * and preventing unnecessary re-rendering of unchanged items.
2875
- *
3069
+ * @protected
2876
3070
  * @param {number} index - The index of the item in the list
2877
3071
  * @param {KeyValue | string | number} item - The item data to track
2878
- * @returns {string | number} A unique identifier for the item
2879
- * @memberOf NgxDecafComponentDirective
3072
+ * @return {string | number} A unique identifier for the item
3073
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
2880
3074
  */
2881
3075
  trackItemFn(index, item) {
2882
3076
  return `${index}-${item}`;
2883
3077
  }
3078
+ /**
3079
+ * @description Handles custom events from child components or DOM elements.
3080
+ * @summary Processes custom events by extracting event handlers and delegating to appropriate
3081
+ * handler classes. Supports both CustomEvent format with detail property and direct event
3082
+ * objects. If specific handlers are defined in the event, it instantiates the handler class
3083
+ * and invokes its handle method. If no handler is found or defined, the event is emitted
3084
+ * up the component hierarchy via the listenEvent output.
3085
+ * @param {IBaseCustomEvent | ICrudFormEvent | CustomEvent} event - The event to handle
3086
+ * @return {Promise<void>} A promise that resolves when event handling is complete
3087
+ * @mermaid
3088
+ * sequenceDiagram
3089
+ * participant C as Child Component
3090
+ * participant D as NgxDecafComponentDirective
3091
+ * participant H as Event Handler
3092
+ * participant P as Parent Component
3093
+ *
3094
+ * C->>D: handleEvent(event)
3095
+ * alt Event is CustomEvent
3096
+ * D->>D: Extract event.detail
3097
+ * end
3098
+ * D->>D: Get event name and handlers
3099
+ * alt Handlers defined
3100
+ * alt Handler exists for event
3101
+ * D->>H: new Handler(router)
3102
+ * D->>H: handle(event)
3103
+ * H-->>D: return result
3104
+ * else No handler found
3105
+ * D->>D: log.debug("No handler found")
3106
+ * end
3107
+ * else No handlers
3108
+ * D->>P: listenEvent.emit(event)
3109
+ * end
3110
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
3111
+ */
2884
3112
  async handleEvent(event) {
2885
3113
  let name = "";
2886
3114
  const log = this.log.for(this.handleEvent);
@@ -2907,8 +3135,70 @@ class NgxDecafComponentDirective extends LoggedClass {
2907
3135
  }
2908
3136
  this.listenEvent.emit(event);
2909
3137
  }
3138
+ /**
3139
+ * @description Determines if a specific operation is allowed in the current context.
3140
+ * @summary This method checks if an operation is included in the list of available
3141
+ * CRUD operations and if it's not the current operation (unless the current operation
3142
+ * is CREATE). This is used to enable/disable or show/hide operation buttons in the UI.
3143
+ * Returns false if the operations array is undefined or the operation is not in the list.
3144
+ * @param {string} operation - The operation to check
3145
+ * @return {boolean} True if the operation is allowed, false otherwise
3146
+ * @mermaid
3147
+ * sequenceDiagram
3148
+ * participant D as NgxDecafComponentDirective
3149
+ * participant U as UI
3150
+ *
3151
+ * U->>D: isAllowed(operation)
3152
+ * alt operations is undefined
3153
+ * D-->>U: Return false
3154
+ * else
3155
+ * D->>D: Check if operation is in operations
3156
+ * D->>D: Check if operation is not current operation
3157
+ * D-->>U: Return result
3158
+ * end
3159
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
3160
+ */
3161
+ isAllowed(operation) {
3162
+ if (!this.operations)
3163
+ return false;
3164
+ return this.operations.includes(operation) && (this.operation !== OperationKeys.CREATE && ((this.operation || "").toLowerCase() !== operation || !this.operation));
3165
+ }
3166
+ /**
3167
+ * @description Navigates to a different operation for the current model.
3168
+ * @summary This method constructs a navigation URL based on the component's base route,
3169
+ * the requested operation, and optionally a model ID. It then uses the Angular router
3170
+ * service to navigate to the constructed URL. This is typically used when switching
3171
+ * between different CRUD operations (create, read, update, delete) on a model.
3172
+ * The URL format is: {route}/{operation}/{id?}
3173
+ * @param {string} operation - The operation to navigate to (e.g., 'create', 'read', 'update', 'delete')
3174
+ * @param {string} [id] - Optional model ID to include in the navigation URL
3175
+ * @return {Promise<boolean>} A promise that resolves to true if navigation was successful
3176
+ * @mermaid
3177
+ * sequenceDiagram
3178
+ * participant U as UI
3179
+ * participant D as NgxDecafComponentDirective
3180
+ * participant R as Router
3181
+ *
3182
+ * U->>D: Click operation button
3183
+ * D->>D: changeOperation(operation, id)
3184
+ * D->>D: Construct navigation URL
3185
+ * Note over D: URL: {route}/{operation}/{id?}
3186
+ * D->>R: navigateByUrl(page)
3187
+ * R->>R: Navigate to new route
3188
+ * R-->>D: Return navigation result
3189
+ * D-->>U: Display new operation view
3190
+ * @memberOf module:lib/engine/NgxDecafComponentDirective
3191
+ */
3192
+ async changeOperation(operation, id) {
3193
+ let page = `${this.route}/${operation}/`.replace('//', '/');
3194
+ if (!id)
3195
+ id = this.modelId;
3196
+ if (this.modelId)
3197
+ page = `${page}${this.modelId || id}`;
3198
+ return this.router.navigateByUrl(page);
3199
+ }
2910
3200
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxDecafComponentDirective, deps: [{ token: CPTKN }, { token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
2911
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxDecafComponentDirective, isStandalone: true, inputs: { name: "name", childOf: "childOf", uid: "uid", model: "model", modelId: "modelId", pk: "pk", mapper: "mapper", operations: "operations", row: "row", col: "col", className: "className", locale: "locale", item: "item", props: "props", route: "route" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
3201
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxDecafComponentDirective, isStandalone: true, inputs: { name: "name", childOf: "childOf", uid: "uid", model: "model", modelId: "modelId", pk: "pk", mapper: "mapper", operations: "operations", operation: "operation", row: "row", col: "col", className: "className", locale: "locale", item: "item", props: "props", route: "route" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
2912
3202
  }
2913
3203
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxDecafComponentDirective, decorators: [{
2914
3204
  type: Directive,
@@ -2938,6 +3228,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
2938
3228
  type: Input
2939
3229
  }], operations: [{
2940
3230
  type: Input
3231
+ }], operation: [{
3232
+ type: Input
2941
3233
  }], row: [{
2942
3234
  type: Input
2943
3235
  }], col: [{
@@ -2957,12 +3249,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
2957
3249
  }] } });
2958
3250
 
2959
3251
  /**
3252
+ * @module lib/engine/NgxDecafFormFieldDirective
3253
+ * @description Base directive for CRUD form fields in Decaf Angular applications.
3254
+ * @summary Provides the NgxDecafFormFieldDirective abstract class that implements ControlValueAccessor
3255
+ * and FieldProperties to enable form field integration with Angular's reactive forms system.
3256
+ * This directive handles form control lifecycle, validation, multi-entry forms, and CRUD operations.
3257
+ */
3258
+ /**
3259
+ * @description Abstract base directive for CRUD form fields in Angular applications.
3260
+ * @summary Provides the foundation for all form field components in Decaf applications by implementing
3261
+ * Angular's ControlValueAccessor interface and FieldProperties for validation. This directive manages
3262
+ * form control integration, validation state, multi-entry forms (FormArrays), and CRUD operation context.
3263
+ * It handles form group lifecycle, error messaging, change detection, and parent-child form relationships.
3264
+ * Extend this class to create custom form field components that seamlessly integrate with Angular's
3265
+ * reactive forms and Decaf's validation system.
2960
3266
  * @class NgxDecafFormFieldDirective
2961
- * @implements {FieldProperties}
3267
+ * @extends {NgxDecafComponentDirective}
2962
3268
  * @implements {ControlValueAccessor}
2963
- * @summary Abstract class representing a CRUD form field for Angular applications
2964
- * @description This class provides the base implementation for CRUD form fields in Angular,
2965
- * implementing both CrudFormField and ControlValueAccessor interfaces.
3269
+ * @implements {FieldProperties}
3270
+ * @example
3271
+ * ```typescript
3272
+ * @Component({
3273
+ * selector: 'app-text-field',
3274
+ * templateUrl: './text-field.component.html',
3275
+ * providers: [{
3276
+ * provide: NG_VALUE_ACCESSOR,
3277
+ * useExisting: forwardRef(() => TextFieldComponent),
3278
+ * multi: true
3279
+ * }]
3280
+ * })
3281
+ * export class TextFieldComponent extends NgxDecafFormFieldDirective {
3282
+ * constructor() {
3283
+ * super();
3284
+ * }
3285
+ * }
3286
+ * ```
2966
3287
  */
2967
3288
  class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
2968
3289
  constructor() {
@@ -2972,40 +3293,51 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
2972
3293
  * @summary When working with multiple form groups (form arrays), this indicates
2973
3294
  * which form group is currently active or being edited. This is used to manage
2974
3295
  * focus and data binding in multi-entry scenarios.
2975
- *
2976
3296
  * @type {number}
2977
3297
  * @default 0
2978
- * @memberOf NgxDecafFormFieldDirective
3298
+ * @public
2979
3299
  */
2980
3300
  this.activeFormGroupIndex = 0;
3301
+ this.operation = OperationKeys.CREATE;
2981
3302
  /**
2982
- * @description Field mapping configuration.
3303
+ * @description Field mapping configuration for options.
2983
3304
  * @summary Defines how fields from the data model should be mapped to properties used by the component.
2984
3305
  * This allows for flexible data binding between the model and the component's display logic.
2985
- *
3306
+ * Can be either a key-value mapping object or a function that performs the mapping.
2986
3307
  * @type {KeyValue | FunctionLike}
2987
- * @memberOf CrudFieldComponent
3308
+ * @public
2988
3309
  */
2989
3310
  this.optionsMapper = {};
3311
+ /**
3312
+ * @description Flag tracking if validation error event has been dispatched.
3313
+ * @summary Prevents duplicate validation error events from being dispatched.
3314
+ * @type {boolean}
3315
+ * @private
3316
+ */
2990
3317
  this.validationErrorEventDispatched = false;
2991
- // protected constructor() {}
2992
3318
  /**
2993
- * @summary String formatting function
2994
- * @description Provides access to the sf function for error message formatting
2995
- * @prop {function(string, ...string): string} sf - String formatting function
3319
+ * @description String formatting utility function.
3320
+ * @summary Provides access to the sf (string format) function for formatting error messages
3321
+ * and other string templates. Used primarily for localizing and parameterizing validation messages.
3322
+ * @type {function(string, ...string): string}
3323
+ * @public
2996
3324
  */
2997
3325
  this.sf = sf;
2998
3326
  /**
2999
- * @summary Change callback function
3000
- * @description Function called when the field value changes
3001
- * @property {function(): unknown} onChange - onChange event handler
3327
+ * @description Callback function invoked when the field value changes.
3328
+ * @summary Function registered by Angular's forms system through registerOnChange.
3329
+ * Called automatically when the field's value is updated to notify the form of the change.
3330
+ * @type {function(): unknown}
3331
+ * @public
3002
3332
  */
3003
3333
  // eslint-disable-next-line @typescript-eslint/no-empty-function
3004
3334
  this.onChange = () => { };
3005
3335
  /**
3006
- * @summary Touch callback function
3007
- * @description Function called when the field is touched
3008
- * @property {function(): unknown} onTouch - onTouch event handler
3336
+ * @description Callback function invoked when the field is touched.
3337
+ * @summary Function registered by Angular's forms system through registerOnTouched.
3338
+ * Called when the field is blurred or otherwise marked as touched.
3339
+ * @type {function(): unknown}
3340
+ * @public
3009
3341
  */
3010
3342
  // eslint-disable-next-line @typescript-eslint/no-empty-function
3011
3343
  this.onTouch = () => { };
@@ -3015,9 +3347,9 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3015
3347
  * @summary Returns the appropriate FormGroup based on whether this field supports
3016
3348
  * multiple values. For single-value fields, returns the main form group.
3017
3349
  * For multi-value fields, returns the form group at the active index from the parent FormArray.
3018
- *
3019
- * @returns {FormGroup} The currently active FormGroup for this field
3020
- * @memberOf CrudFieldComponent
3350
+ * If no formGroup is set, returns the parent of the formControl.
3351
+ * @return {FormGroup} The currently active FormGroup for this field
3352
+ * @public
3021
3353
  */
3022
3354
  get activeFormGroup() {
3023
3355
  if (!this.formGroup)
@@ -3028,53 +3360,61 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3028
3360
  return this.formGroup;
3029
3361
  }
3030
3362
  return this.formGroup;
3031
- // const formGroup = this.formGroup as FormGroup;
3032
- // try {
3033
- // const root = formGroup.root as FormGroup;
3034
- // return this.multiple
3035
- // ? ((root?.controls?.[this.name] as FormArray)?.at(this.activeFormGroupIndex) as FormGroup)
3036
- // : formGroup;
3037
- // } catch (error: unknown) {
3038
- // this.log.error("Error getting active form group:", error as Error);
3039
- // return formGroup;
3040
- // }
3041
3363
  }
3042
3364
  /**
3043
- * @summary Write value to the field
3044
- * @description Sets the value of the field
3365
+ * @description Writes a value to the form field.
3366
+ * @summary Part of Angular's ControlValueAccessor interface. Sets the field's value
3367
+ * when the form programmatically updates it. This is called by Angular forms when
3368
+ * the model value changes.
3045
3369
  * @param {string} obj - The value to be set
3370
+ * @return {void}
3371
+ * @public
3046
3372
  */
3047
3373
  writeValue(obj) {
3048
3374
  this.value = obj;
3049
3375
  }
3050
3376
  /**
3051
- * @summary Register change callback
3052
- * @description Registers a function to be called when the field value changes
3377
+ * @description Registers the onChange callback function.
3378
+ * @summary Part of Angular's ControlValueAccessor interface. Stores the function
3379
+ * that Angular forms provides to be called when the field value changes.
3053
3380
  * @param {function(): unknown} fn - The function to be called on change
3381
+ * @return {void}
3382
+ * @public
3054
3383
  */
3055
3384
  registerOnChange(fn) {
3056
3385
  this.onChange = fn;
3057
3386
  }
3058
3387
  /**
3059
- * @summary Register touch callback
3060
- * @description Registers a function to be called when the field is touched
3388
+ * @description Registers the onTouched callback function.
3389
+ * @summary Part of Angular's ControlValueAccessor interface. Stores the function
3390
+ * that Angular forms provides to be called when the field is touched/blurred.
3061
3391
  * @param {function(): unknown} fn - The function to be called on touch
3392
+ * @return {void}
3393
+ * @public
3062
3394
  */
3063
3395
  registerOnTouched(fn) {
3064
3396
  this.onTouch = fn;
3065
3397
  }
3066
3398
  /**
3067
- * @summary Set disabled state
3068
- * @description Sets the disabled state of the field
3399
+ * @description Sets the disabled state of the field.
3400
+ * @summary Part of Angular's ControlValueAccessor interface. Called by Angular forms
3401
+ * when the disabled state of the control changes.
3069
3402
  * @param {boolean} isDisabled - Whether the field should be disabled
3403
+ * @return {void}
3404
+ * @public
3070
3405
  */
3071
3406
  setDisabledState(isDisabled) {
3072
3407
  this.disabled = isDisabled;
3073
3408
  }
3074
3409
  /**
3075
- * @summary After view initialization logic
3076
- * @description Performs necessary setup after the view has been initialized
3077
- * @returns {HTMLElement} The parent element of the field
3410
+ * @description Performs setup after the view has been initialized.
3411
+ * @summary Retrieves and returns the parent HTML element based on the current CRUD operation.
3412
+ * For READ and DELETE operations, returns the immediate parent element. For CREATE and UPDATE
3413
+ * operations, finds the parent div element and registers it with the form service.
3414
+ * @return {HTMLElement} The parent element of the field
3415
+ * @throws {RenderingError} If unable to retrieve parent form element for CREATE/UPDATE operations
3416
+ * @throws {InternalError} If the operation is invalid
3417
+ * @public
3078
3418
  */
3079
3419
  afterViewInit() {
3080
3420
  let parent;
@@ -3096,6 +3436,16 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3096
3436
  throw new InternalError(`Invalid operation: ${this.operation}`);
3097
3437
  }
3098
3438
  }
3439
+ /**
3440
+ * @description Angular lifecycle hook for detecting input property changes.
3441
+ * @summary Overrides the parent ngOnChanges to handle changes to activeFormGroupIndex and value.
3442
+ * When activeFormGroupIndex changes in a multiple field scenario, updates the active form group
3443
+ * and form control. When value changes, updates the form control value. Delegates to parent
3444
+ * implementation for initial change detection.
3445
+ * @param {SimpleChanges} changes - Object containing the changed properties
3446
+ * @return {void}
3447
+ * @public
3448
+ */
3099
3449
  ngOnChanges(changes) {
3100
3450
  if (!this.initialized)
3101
3451
  super.ngOnChanges(changes);
@@ -3109,14 +3459,38 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3109
3459
  && (changes['value'].currentValue !== undefined && changes['value'].currentValue !== this.value))
3110
3460
  this.setValue(changes['value'].currentValue);
3111
3461
  }
3462
+ /**
3463
+ * @description Cleanup logic when the component is destroyed.
3464
+ * @summary Unregisters the form group from the form service to prevent memory leaks
3465
+ * and clean up form references.
3466
+ * @return {void}
3467
+ * @public
3468
+ */
3112
3469
  onDestroy() {
3113
3470
  if (this.formGroup)
3114
3471
  NgxDecafFormService.unregister(this.formGroup);
3115
3472
  }
3473
+ /**
3474
+ * @description Sets the value of the form control.
3475
+ * @summary Updates the form control's value and triggers validation. This is used
3476
+ * when the value needs to be programmatically updated from outside the form control.
3477
+ * @param {unknown} value - The value to set
3478
+ * @return {void}
3479
+ * @public
3480
+ */
3116
3481
  setValue(value) {
3117
3482
  this.formControl.setValue(value);
3118
3483
  this.formControl.updateValueAndValidity();
3119
3484
  }
3485
+ /**
3486
+ * @description Retrieves validation error messages for the field.
3487
+ * @summary Checks the form control for validation errors and returns formatted error messages.
3488
+ * If errors exist, dispatches a validation error event to parent accordion components.
3489
+ * Error messages are translated and formatted with relevant field properties.
3490
+ * @param {HTMLElement} parent - The parent HTML element used to find accordion components
3491
+ * @return {string | void} Formatted error message string, or void if no errors
3492
+ * @public
3493
+ */
3120
3494
  getErrors(parent) {
3121
3495
  const formControl = this.formControl;
3122
3496
  if (formControl) {
@@ -3142,12 +3516,15 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3142
3516
  }
3143
3517
  }
3144
3518
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxDecafFormFieldDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3145
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxDecafFormFieldDirective, isStandalone: true, inputs: { activeFormGroupIndex: "activeFormGroupIndex", parentComponent: "parentComponent", optionsMapper: "optionsMapper" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
3519
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxDecafFormFieldDirective, isStandalone: true, inputs: { activeFormGroupIndex: "activeFormGroupIndex", operation: "operation", parentComponent: "parentComponent", optionsMapper: "optionsMapper" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
3146
3520
  }
3147
3521
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxDecafFormFieldDirective, decorators: [{
3148
3522
  type: Directive
3149
3523
  }], ctorParameters: () => [], propDecorators: { activeFormGroupIndex: [{
3150
3524
  type: Input
3525
+ }], operation: [{
3526
+ type: Input,
3527
+ args: [{ required: true }]
3151
3528
  }], parentComponent: [{
3152
3529
  type: Input
3153
3530
  }], optionsMapper: [{
@@ -3166,32 +3543,112 @@ class NgxEventHandler extends LoggedClass {
3166
3543
  }
3167
3544
 
3168
3545
  /**
3169
- * @module module:lib/engine/NgxPageDirective
3546
+ * @module lib/engine/NgxPageDirective
3170
3547
  * @description Base page component for Decaf Angular applications.
3171
3548
  * @summary Provides a page-level base class (NgxPageDirective) that extends NgxDecafComponentDirective and
3172
3549
  * offers page-focused utilities such as menu management, title handling and router event hooks.
3173
- *
3174
3550
  * @link {@link NgxPageDirective}
3175
3551
  */
3552
+ /**
3553
+ * @description Base directive for page-level components in Decaf Angular applications.
3554
+ * @summary Abstract directive that provides foundational functionality for page components.
3555
+ * Extends NgxDecafComponentDirective to add page-specific features including menu management,
3556
+ * page title handling, and Ionic lifecycle hooks. This directive serves as the base class for
3557
+ * all page-level components, providing consistent behavior for navigation, routing, and UI state.
3558
+ * @class NgxPageDirective
3559
+ * @extends {NgxDecafComponentDirective}
3560
+ * @memberOf module:lib/engine/NgxPageDirective
3561
+ */
3176
3562
  class NgxPageDirective extends NgxDecafComponentDirective {
3563
+ /**
3564
+ * @description Constructor for NgxPageDirective.
3565
+ * @summary Initializes the page directive with optional locale root and menu visibility settings.
3566
+ * Calls the parent NgxDecafComponentDirective constructor to set up base functionality including
3567
+ * logging, localization, and component identification.
3568
+ * @param {string} [localeRoot] - Optional locale root key for internationalization
3569
+ * @param {boolean} [hasMenu=true] - Whether this page should display the menu
3570
+ * @memberOf module:lib/engine/NgxPageDirective
3571
+ */
3177
3572
  // eslint-disable-next-line @angular-eslint/prefer-inject
3178
3573
  constructor(localeRoot, hasMenu = true) {
3179
3574
  super(localeRoot);
3180
3575
  this.localeRoot = localeRoot;
3181
3576
  this.hasMenu = hasMenu;
3577
+ /**
3578
+ * @description Page title text for the current view.
3579
+ * @summary Stores the title text to be displayed for this page. This can be set dynamically
3580
+ * based on the current route or menu configuration and is used to update the browser's
3581
+ * title bar or page header.
3582
+ * @type {string}
3583
+ * @default ''
3584
+ * @memberOf module:lib/engine/NgxPageDirective
3585
+ */
3182
3586
  this.title = '';
3587
+ /**
3588
+ * @description Menu items array for page navigation.
3589
+ * @summary Contains the collection of menu items available for this page. Each menu item
3590
+ * defines a navigation option with properties like label, URL, icon, and visibility settings.
3591
+ * This array is used to construct the application's navigation menu and can be filtered or
3592
+ * customized per page.
3593
+ * @protected
3594
+ * @type {IMenuItem[]}
3595
+ * @default []
3596
+ * @memberOf module:lib/engine/NgxPageDirective
3597
+ */
3183
3598
  this.menu = [];
3599
+ /**
3600
+ * @description Angular Title service for browser title management.
3601
+ * @summary Injected service that provides control over the browser's document title.
3602
+ * Used to dynamically set the page title based on the current route or active menu item,
3603
+ * improving SEO and user experience.
3604
+ * @protected
3605
+ * @type {Title}
3606
+ * @memberOf module:lib/engine/NgxPageDirective
3607
+ */
3184
3608
  this.titleService = inject(Title);
3185
3609
  }
3186
- async ionViewWillEnter() {
3610
+ /**
3611
+ * @description Ionic lifecycle hook called when the page is about to enter view.
3612
+ * @summary This lifecycle hook is triggered just before the page becomes visible to the user.
3613
+ * It enables or disables the application menu based on the hasMenu property, allowing pages
3614
+ * to control whether the menu should be accessible. This is useful for pages like login screens
3615
+ * where the menu should be hidden.
3616
+ * @return {Promise<void>} A promise that resolves when menu state is updated
3617
+ * @memberOf module:lib/engine/NgxPageDirective
3618
+ */
3619
+ async ngAfterViewInit() {
3620
+ this.router.events.subscribe(async (event) => {
3621
+ if (event instanceof NavigationEnd) {
3622
+ const url = (event?.url || "").replace('/', '');
3623
+ this.hasMenu = url !== "login" && url !== "";
3624
+ this.setPageTitle(url);
3625
+ }
3626
+ if (event instanceof NavigationStart)
3627
+ removeFocusTrap();
3628
+ });
3187
3629
  await this.menuController.enable(this.hasMenu);
3188
3630
  }
3631
+ /**
3632
+ * @description Sets the browser page title based on the current route.
3633
+ * @summary Updates the browser's document title by finding the active menu item that matches
3634
+ * the provided route. If a matching menu item is found, it sets the title using the format
3635
+ * "Decaf For Angular - {menu title or label}". This improves SEO and provides clear context
3636
+ * to users about the current page. If a custom menu array is provided, it uses that instead
3637
+ * of the component's default menu.
3638
+ * @protected
3639
+ * @param {string} route - The current route path to match against menu items
3640
+ * @param {IMenuItem[]} [menu] - Optional custom menu array to search (uses this.menu if not provided)
3641
+ * @return {void}
3642
+ * @memberOf module:lib/engine/NgxPageDirective
3643
+ */
3189
3644
  setPageTitle(route, menu) {
3645
+ if (!route)
3646
+ route = this.router.url.replace('/', '');
3190
3647
  if (menu)
3191
3648
  menu = this.menu;
3192
3649
  const activeMenu = this.menu.find(item => item?.url?.includes(route));
3193
3650
  if (activeMenu)
3194
- this.titleService.setTitle(`Decaf For Angular - ${activeMenu?.title || activeMenu?.label}`);
3651
+ this.titleService.setTitle(`${activeMenu?.title || activeMenu?.label}`);
3195
3652
  }
3196
3653
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxPageDirective, deps: [{ token: CPTKN }, { token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
3197
3654
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxPageDirective, isStandalone: true, usesInheritance: true, ngImport: i0 }); }
@@ -3778,7 +4235,6 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxDecafFormFieldDirec
3778
4235
  if (this.optionsMapper) {
3779
4236
  if (this.optionsMapper instanceof Function || typeof this.optionsMapper === 'function') {
3780
4237
  const mapper = this.optionsMapper;
3781
- console.log(this.options);
3782
4238
  this.options = this.options.map((option) => {
3783
4239
  return mapper(option);
3784
4240
  });
@@ -3847,48 +4303,6 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxDecafFormFieldDirec
3847
4303
  return false;
3848
4304
  return this.formControl.value.includes(value);
3849
4305
  }
3850
- /**
3851
- * @description Handles fieldset group creation events from parent fieldsets.
3852
- * @summary Processes events triggered when a new group needs to be added to a fieldset.
3853
- * Validates the current form group, checks for uniqueness if applicable, and either
3854
- * creates a new group or provides validation feedback. Updates the active form group
3855
- * and resets the field for new input after successful creation.
3856
- *
3857
- * @param {CustomEvent} event - The fieldset create group event containing group details
3858
- * @returns {void}
3859
- * @memberOf CrudFieldComponent
3860
- */
3861
- handleFieldsetCreateGroupEvent(event) {
3862
- event.stopImmediatePropagation();
3863
- const { formGroup } = event.detail;
3864
- // const formGroup = this.formGroup as FormGroup;
3865
- // const parentFormGroup = this.formGroup?.parent as FormArray;
3866
- // const isValid = NgxDecafFormService.validateFields(formGroup as FormGroup);
3867
- // const indexToCheck = operation === OperationKeys.CREATE ?
3868
- // index === 0 ? index : parentFormGroup.length - 1 : index;
3869
- // const isUnique = NgxDecafFormService.isUniqueOnGroup(formGroup, indexToCheck, operation || OperationKeys.CREATE);
3870
- // event = new CustomEvent(EventConstants.FIELDSET_ADD_GROUP, {
3871
- // detail: {isValid: isValid && isUnique, value: formGroup.value, formGroup: parentFormGroup, formService: NgxDecafFormService},
3872
- // });
3873
- // component.dispatchEvent(event);
3874
- // if(isValid && isUnique) {
3875
- // const newIndex = parentFormGroup?.length;
3876
- // if(operation === OperationKeys.CREATE) {
3877
- // NgxDecafFormService.addGroupToParent(parentFormGroup?.parent as FormGroup, parent, newIndex);
3878
- // this.activeFormGroup = newIndex;
3879
- // } else {
3880
- // this.activeFormGroup = newIndex - 1;
3881
- // }
3882
- // this.formGroup = this.activeFormGroup;
3883
- // // NgxDecafFormService.reset(this.formGroup as FormGroup);
3884
- // this.formControl = (this.formGroup as FormGroup).get(this.name) as FormControl;
3885
- // // NgxDecafFormService.reset(this.formControl);
3886
- // // this.component.nativeElement.setFocus();
3887
- // } else {
3888
- // if(isUnique)
3889
- // this.component.nativeElement.setFocus();
3890
- // }
3891
- }
3892
4306
  /**
3893
4307
  * @description Handles fieldset group update events from parent fieldsets.
3894
4308
  * @summary Processes events triggered when an existing group needs to be updated.
@@ -3906,31 +4320,8 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxDecafFormFieldDirec
3906
4320
  this.formControl = this.formGroup.get(this.name);
3907
4321
  this.value = this.formControl.value;
3908
4322
  }
3909
- /**
3910
- * @description Handles fieldset group removal events from parent fieldsets.
3911
- * @summary Processes events triggered when a group needs to be removed from a fieldset.
3912
- * Removes the specified group from the form array, updates the active form group index,
3913
- * and refreshes the form references. Dispatches a confirmation event back to the component.
3914
- *
3915
- * @param {CustomEvent} event - The fieldset remove group event containing removal details
3916
- * @returns {void}
3917
- * @memberOf CrudFieldComponent
3918
- */
3919
- handleFieldsetRemoveGroupEvent(event) {
3920
- const { component, index } = event.detail;
3921
- const formArray = this.formGroup?.parent;
3922
- formArray.removeAt(index);
3923
- this.activeFormGroupIndex = formArray.length === 1 ? 0 : formArray.length - 1;
3924
- this.formGroup = this.activeFormGroup;
3925
- this.formControl = this.formGroup.get(this.name);
3926
- this.parentComponent = formArray;
3927
- event = new CustomEvent(EventConstants.FIELDSET_REMOVE_GROUP, {
3928
- detail: { value: true },
3929
- });
3930
- component.dispatchEvent(event);
3931
- }
3932
4323
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
3933
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", className: "className", 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", 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" }, host: { listeners: { "window:fieldsetAddGroupEvent": "handleFieldsetCreateGroupEvent($event)", "window:fieldsetUpdateGroupEvent": "handleFieldsetUpdateGroupEvent($event)", "window:fieldsetRemoveGroupEvent": "handleFieldsetRemoveGroupEvent($event)" }, properties: { "attr.id": "uid", "attr.class": "className" } }, 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 @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\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 @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\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 >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\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 } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.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,.dcf-input-item.delete{padding:0;margin:0!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!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}.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}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-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-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.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: "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"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
4324
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", className: "className", 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", 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" }, host: { listeners: { "window:fieldsetUpdateGroupEvent": "handleFieldsetUpdateGroupEvent($event)" }, properties: { "attr.id": "uid", "attr.class": "className" } }, 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 @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\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 @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\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 >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\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 } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.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,.dcf-input-item.delete{padding:0;margin:0!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!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}.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}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-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-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.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: "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"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
3934
4325
  };
3935
4326
  CrudFieldComponent = __decorate([
3936
4327
  Dynamic()
@@ -3950,7 +4341,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
3950
4341
  IonLabel,
3951
4342
  IonText,
3952
4343
  IonTextarea
3953
- ], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { '[attr.id]': 'uid', '[attr.class]': 'className' }, 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 @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\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 @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\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 >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\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 } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.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,.dcf-input-item.delete{padding:0;margin:0!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!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}.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}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-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-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"] }]
4344
+ ], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { '[attr.id]': 'uid', '[attr.class]': 'className' }, 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 @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\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 @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\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 >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\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 } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.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,.dcf-input-item.delete{padding:0;margin:0!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!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}.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}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-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-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"] }]
3954
4345
  }], propDecorators: { operation: [{
3955
4346
  type: Input,
3956
4347
  args: [{ required: true }]
@@ -4049,15 +4440,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
4049
4440
  type: Input
4050
4441
  }], translatable: [{
4051
4442
  type: Input
4052
- }], handleFieldsetCreateGroupEvent: [{
4053
- type: HostListener,
4054
- args: ['window:fieldsetAddGroupEvent', ['$event']]
4055
4443
  }], handleFieldsetUpdateGroupEvent: [{
4056
4444
  type: HostListener,
4057
4445
  args: ['window:fieldsetUpdateGroupEvent', ['$event']]
4058
- }], handleFieldsetRemoveGroupEvent: [{
4059
- type: HostListener,
4060
- args: ['window:fieldsetRemoveGroupEvent', ['$event']]
4061
4446
  }] } });
4062
4447
 
4063
4448
  /**
@@ -4078,7 +4463,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
4078
4463
  * @class NgxParentComponentDirective
4079
4464
  * @extends {NgxParentComponentDirective}
4080
4465
  * @implements {OnInit}
4081
- * @memberOf NgxParentComponentDirective
4082
4466
  */
4083
4467
  class NgxParentComponentDirective extends NgxDecafComponentDirective {
4084
4468
  constructor() {
@@ -4091,7 +4475,6 @@ class NgxParentComponentDirective extends NgxDecafComponentDirective {
4091
4475
  * page assignment, and display properties.
4092
4476
  *
4093
4477
  * @type {UIModelMetadata[]}
4094
- * @memberOf NgxParentComponentDirective
4095
4478
  */
4096
4479
  this.children = [];
4097
4480
  /**
@@ -4103,7 +4486,6 @@ class NgxParentComponentDirective extends NgxDecafComponentDirective {
4103
4486
  *
4104
4487
  * @type {(number | string[])}
4105
4488
  * @default 1
4106
- * @memberOf NgxParentComponentDirective
4107
4489
  */
4108
4490
  this.cols = 1;
4109
4491
  /**
@@ -4114,7 +4496,6 @@ class NgxParentComponentDirective extends NgxDecafComponentDirective {
4114
4496
  *
4115
4497
  * @type {(number | string[])}
4116
4498
  * @default 1
4117
- * @memberOf NgxParentComponentDirective
4118
4499
  */
4119
4500
  this.rows = 1;
4120
4501
  }
@@ -4143,17 +4524,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4143
4524
  constructor() {
4144
4525
  super(...arguments);
4145
4526
  this.crudFieldComponent = ComponentsTagNames.CRUD_FIELD;
4146
- /**
4147
- * @description Angular Location service.
4148
- * @summary Injected service that provides access to the browser's URL and history.
4149
- * This service is used for interacting with the browser's history API, allowing
4150
- * for back navigation and URL manipulation outside of Angular's router.
4151
- *
4152
- * @private
4153
- * @type {Location}
4154
- * @memberOf CrudFormComponent
4155
- */
4156
- this.location = inject(Location);
4157
4527
  /**
4158
4528
  * @description Field update trigger mode for form validation.
4159
4529
  * @summary Determines when form field validation should be triggered. Options include
@@ -4162,7 +4532,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4162
4532
  *
4163
4533
  * @type {FieldUpdateMode}
4164
4534
  * @default 'change'
4165
- * @memberOf CrudFormComponent
4166
4535
  */
4167
4536
  this.updateOn = 'change';
4168
4537
  /**
@@ -4173,7 +4542,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4173
4542
  *
4174
4543
  * @type {HTMLFormTarget}
4175
4544
  * @default '_self'
4176
- * @memberOf CrudFormComponent
4177
4545
  */
4178
4546
  this.target = '_self';
4179
4547
  /**
@@ -4184,9 +4552,18 @@ class NgxFormDirective extends NgxParentComponentDirective {
4184
4552
  *
4185
4553
  * @type {'get' | 'post' | 'event'}
4186
4554
  * @default 'event'
4187
- * @memberOf CrudFormComponent
4188
4555
  */
4189
4556
  this.method = 'event';
4557
+ /**
4558
+ * @description The current CRUD operation being performed.
4559
+ * @summary Specifies the type of operation this form is handling (CREATE, READ, UPDATE, DELETE).
4560
+ * This is a required input that determines form behavior, validation rules, and available actions.
4561
+ * The operation affects form state, button visibility, and submission logic.
4562
+ *
4563
+ * @type {CrudOperations}
4564
+ * @required
4565
+ */
4566
+ this.operation = OperationKeys.CREATE;
4190
4567
  /**
4191
4568
  * @description Angular reactive FormGroup for form state management.
4192
4569
  * @summary The FormGroup instance that manages all form controls, validation,
@@ -4194,7 +4571,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4194
4571
  * controlling form behavior. May be undefined for read-only operations.
4195
4572
  *
4196
4573
  * @type {FormGroup | undefined}
4197
- * @memberOf CrudFormComponent
4198
4574
  */
4199
4575
  this.formGroup = undefined;
4200
4576
  /**
@@ -4204,7 +4580,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4204
4580
  * components. This enables decoupled handling of form submission logic.
4205
4581
  *
4206
4582
  * @type {EventEmitter<ICrudFormEvent>}
4207
- * @memberOf CrudFormComponent
4208
4583
  */
4209
4584
  this.submitEvent = new EventEmitter();
4210
4585
  /**
@@ -4216,7 +4591,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4216
4591
  *
4217
4592
  * @type {string}
4218
4593
  * @default Randomly generated 12-character string
4219
- * @memberOf CrudFormComponent
4220
4594
  */
4221
4595
  this.allowClear = true;
4222
4596
  // /**
@@ -4252,18 +4626,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4252
4626
  // * @memberOf CrudFormComponent
4253
4627
  // */
4254
4628
  // protected translateService: TranslateService = inject(TranslateService);
4255
- /**
4256
- * @description Reference to CRUD operation constants for template usage.
4257
- * @summary Exposes the OperationKeys enum to the component template, enabling
4258
- * conditional rendering and behavior based on operation types. This protected
4259
- * readonly property ensures that template logic can access operation constants
4260
- * while maintaining encapsulation and preventing accidental modification.
4261
- *
4262
- * @protected
4263
- * @readonly
4264
- * @memberOf CrudFormComponent
4265
- */
4266
- this.OperationKeys = OperationKeys;
4267
4629
  this.activeFormGroupIndex = 0;
4268
4630
  }
4269
4631
  get activeFormGroup() {
@@ -4277,13 +4639,13 @@ class NgxFormDirective extends NgxParentComponentDirective {
4277
4639
  * form input. Configuration options are merged with default settings.
4278
4640
  *
4279
4641
  * @returns {Promise<void>}
4280
- * @memberOf CrudFormComponent
4281
4642
  */
4282
4643
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
4283
4644
  async ngOnInit(model) {
4284
4645
  // dont call super.ngOnInit to model conflicts
4285
4646
  if (this.operation === OperationKeys.READ || this.operation === OperationKeys.DELETE)
4286
4647
  this.formGroup = undefined;
4648
+ this.initialized = true;
4287
4649
  }
4288
4650
  /**
4289
4651
  * @description Component cleanup lifecycle method.
@@ -4292,7 +4654,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4292
4654
  * and ensure proper resource cleanup.
4293
4655
  *
4294
4656
  * @returns {void}
4295
- * @memberOf CrudFormComponent
4296
4657
  */
4297
4658
  ngOnDestroy() {
4298
4659
  if (this.formGroup)
@@ -4330,7 +4691,6 @@ class NgxFormDirective extends NgxParentComponentDirective {
4330
4691
  * since these operations don't have modifiable form data to reset.
4331
4692
  *
4332
4693
  * @returns {void}
4333
- * @memberOf CrudFormComponent
4334
4694
  */
4335
4695
  handleReset() {
4336
4696
  if (![OperationKeys.DELETE, OperationKeys.READ].includes(this.operation) && this.allowClear)
@@ -4554,8 +4914,7 @@ let LayoutComponent = class LayoutComponent extends NgxParentComponentDirective
4554
4914
  * @memberOf LayoutComponent
4555
4915
  */
4556
4916
  constructor() {
4557
- super();
4558
- this.initializeProps = true;
4917
+ super('LayoutComponent');
4559
4918
  /**
4560
4919
  * @description Media breakpoint for responsive behavior.
4561
4920
  * @summary Determines the responsive breakpoint at which the layout should adapt.
@@ -4568,7 +4927,6 @@ let LayoutComponent = class LayoutComponent extends NgxParentComponentDirective
4568
4927
  * @memberOf LayoutComponent
4569
4928
  */
4570
4929
  this.gap = 'collapse';
4571
- this.match = true;
4572
4930
  /**
4573
4931
  * @description Media breakpoint for responsive behavior.
4574
4932
  * @summary Determines the responsive breakpoint at which the layout should adapt.
@@ -4581,7 +4939,30 @@ let LayoutComponent = class LayoutComponent extends NgxParentComponentDirective
4581
4939
  * @memberOf LayoutComponent
4582
4940
  */
4583
4941
  this.breakpoint = UIMediaBreakPoints.MEDIUM;
4584
- this.componentName = 'LayoutComponent';
4942
+ /**
4943
+ * @description Media breakpoint for responsive behavior.
4944
+ * @summary Determines the responsive breakpoint at which the layout should adapt.
4945
+ * This affects how the grid behaves on different screen sizes, allowing for
4946
+ * mobile-first or desktop-first responsive design patterns. The breakpoint
4947
+ * is automatically processed to ensure compatibility with the UI framework.
4948
+ *
4949
+ * @type {UIMediaBreakPointsType}
4950
+ * @default 'medium'
4951
+ * @memberOf LayoutComponent
4952
+ */
4953
+ this.grid = true;
4954
+ /**
4955
+ * @description Media breakpoint for responsive behavior.
4956
+ * @summary Determines the responsive breakpoint at which the layout should adapt.
4957
+ * This affects how the grid behaves on different screen sizes, allowing for
4958
+ * mobile-first or desktop-first responsive design patterns. The breakpoint
4959
+ * is automatically processed to ensure compatibility with the UI framework.
4960
+ *
4961
+ * @type {UIMediaBreakPointsType}
4962
+ * @default 'medium'
4963
+ * @memberOf LayoutComponent
4964
+ */
4965
+ this.match = true;
4585
4966
  }
4586
4967
  /**
4587
4968
  * @description Getter that converts columns input to an array format.
@@ -4616,10 +4997,13 @@ let LayoutComponent = class LayoutComponent extends NgxParentComponentDirective
4616
4997
  if (typeof rows === Primitives.NUMBER)
4617
4998
  rows = Array.from({ length: Number(rows) }, () => ({ title: '' }));
4618
4999
  return rows.map((row, index) => {
5000
+ const rowsLength = this.rows;
4619
5001
  return {
4620
5002
  title: typeof row === Primitives.STRING ? row : row?.['title'] || "",
4621
5003
  cols: this.children.filter((child) => {
4622
- const row = child.props?.['row'] ?? 1;
5004
+ let row = child.props?.['row'] ?? 1;
5005
+ if (row > rowsLength)
5006
+ row = rowsLength;
4623
5007
  child['col'] = child.props?.['col'] ?? this.cols?.length ?? 1;
4624
5008
  if (row === index + 1)
4625
5009
  return child;
@@ -4641,10 +5025,14 @@ let LayoutComponent = class LayoutComponent extends NgxParentComponentDirective
4641
5025
  this.breakpoint = `@${this.breakpoint}`.toLowerCase();
4642
5026
  this.cols = this._cols;
4643
5027
  this.rows = this._rows;
5028
+ // if(this._rows.length === 1)
5029
+ // this.match = false;
5030
+ // if(this._cols.length === 1)
5031
+ // this.grid = false;
4644
5032
  this.initialized = true;
4645
5033
  }
4646
5034
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: LayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4647
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: LayoutComponent, isStandalone: true, selector: "ngx-decaf-layout", inputs: { initializeProps: "initializeProps", gap: "gap", match: "match", breakpoint: "breakpoint" }, usesInheritance: true, ngImport: i0, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\"'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\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 [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\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: "component", type: ModelRendererComponent, selector: "ngx-decaf-model-renderer", inputs: ["globals", "projectable", "rendererId"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
5035
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: LayoutComponent, isStandalone: true, selector: "ngx-decaf-layout", inputs: { gap: "gap", breakpoint: "breakpoint", grid: "grid", match: "match" }, usesInheritance: true, ngImport: i0, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\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 [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\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: "component", type: ModelRendererComponent, selector: "ngx-decaf-model-renderer", inputs: ["globals", "projectable", "rendererId"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
4648
5036
  };
4649
5037
  LayoutComponent = __decorate([
4650
5038
  Dynamic(),
@@ -4652,21 +5040,20 @@ LayoutComponent = __decorate([
4652
5040
  ], LayoutComponent);
4653
5041
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: LayoutComponent, decorators: [{
4654
5042
  type: Component,
4655
- args: [{ selector: 'ngx-decaf-layout', imports: [TranslatePipe, ModelRendererComponent, ComponentRendererComponent], standalone: true, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\"'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\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 [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\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"] }]
4656
- }], ctorParameters: () => [], propDecorators: { initializeProps: [{
5043
+ args: [{ selector: 'ngx-decaf-layout', imports: [TranslatePipe, ModelRendererComponent, ComponentRendererComponent], standalone: true, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\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 [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\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"] }]
5044
+ }], ctorParameters: () => [], propDecorators: { gap: [{
4657
5045
  type: Input
4658
- }], gap: [{
5046
+ }], breakpoint: [{
4659
5047
  type: Input
4660
- }], match: [{
5048
+ }], grid: [{
4661
5049
  type: Input
4662
- }], breakpoint: [{
5050
+ }], match: [{
4663
5051
  type: Input
4664
5052
  }] } });
4665
5053
 
4666
5054
  let CrudFormComponent = class CrudFormComponent extends NgxFormDirective {
4667
5055
  constructor() {
4668
- super();
4669
- this.componentName = 'CrudFormComponent';
5056
+ super('CrudFormComponent');
4670
5057
  }
4671
5058
  /**
4672
5059
  * @description Component initialization lifecycle method.
@@ -4679,7 +5066,6 @@ let CrudFormComponent = class CrudFormComponent extends NgxFormDirective {
4679
5066
  * @memberOf CrudFormComponent
4680
5067
  */
4681
5068
  async ngOnInit() {
4682
- // console.log(this.formGroup);
4683
5069
  this.options = Object.assign({}, DefaultFormReactiveOptions, this.options || {});
4684
5070
  await super.ngOnInit();
4685
5071
  }
@@ -4715,7 +5101,7 @@ let CrudFormComponent = class CrudFormComponent extends NgxFormDirective {
4715
5101
  });
4716
5102
  }
4717
5103
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4718
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: CrudFormComponent, isStandalone: true, selector: "ngx-decaf-crud-form", host: { properties: { "attr.id": "uid" } }, usesInheritance: true, ngImport: i0, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form\n [class]=\"'dcf-grid dcf-grid-small dcf-child-width-1-1 dcf-form-grid ' + operation\" #component\n [id]=\"rendererId\"\n [formGroup]=\"formGroup\"\n (submit)=\"submit($event)\"\n novalidate\n [target]=\"target\">\n @if(!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n <ngx-decaf-layout\n [children]=\"children || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [initializeProps]=\"false\"\n [rows]=\"rows\"\n [cols]=\"cols\" />\n\n <!-- @for (child of children; track $index) {\n <ngx-decaf-component-renderer\n [class]=\"(child?.props?.className ? 'dcf-form-item ' + child.props.className : 'dcf-form-item')\"\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n [formRoot]=\"formGroup\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n } -->\n }\n <div class=\"dcf-buttons-grid 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 <section [class]=\"'dcf-grid dcf-grid-small dcf-child-width-1-1 dcf-form-grid ' + operation\" #component [id]=\"uid\">\n @if(!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n @for (child of children; track $index) {\n <div [class]=\"(child?.props?.className ? 'dcf-form-item ' + child.props.className : 'dcf-form-item')\">\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n </div>\n }\n }\n </section>\n <section [class]=\"'dcf-buttons-grid 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 </section>\n}\n\n", styles: [".dcf-buttons-grid{margin-top:.5rem;margin-bottom:0}.dcf-buttons-grid.read{margin-bottom:1rem}@media (min-width: 577px){.dcf-buttons-grid.dcf-flex{flex-direction:row-reverse}}@media (max-width: 576px){.dcf-buttons-grid.dcf-flex>div{width:100%}.dcf-buttons-grid.dcf-flex>div+div{margin-top:1rem}.dcf-buttons-grid.dcf-flex ion-button{width:100%}}.dcf-form-grid{padding:1rem}.dcf-form-grid .dcf-form-item{margin-bottom:1.8rem;margin-top:0!important}.dcf-form-grid.read .dcf-form-item{margin-bottom:1rem}::ng-deep ngx-decaf-stepped-form .dcf-input-item{margin-bottom:1.8rem;margin-top:0!important}::ng-deep ngx-decaf-fieldset .dcf-input-item{margin-bottom:1.8rem;margin-top:0!important}\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: LayoutComponent, selector: "ngx-decaf-layout", inputs: ["initializeProps", "gap", "match", "breakpoint"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { 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"] }] }); }
5104
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: CrudFormComponent, isStandalone: true, selector: "ngx-decaf-crud-form", host: { properties: { "attr.id": "uid" } }, usesInheritance: true, ngImport: i0, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form\n [class]=\"'dcf-form-grid ' + operation\" #component\n [id]=\"rendererId\"\n [formGroup]=\"formGroup\"\n (submit)=\"submit($event)\"\n novalidate\n [target]=\"target\">\n @if(!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n <ngx-decaf-layout\n [children]=\"children || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [rows]=\"rows\"\n [cols]=\"cols\" />\n\n <!-- @for (child of children; track $index) {\n <ngx-decaf-component-renderer\n [class]=\"(child?.props?.className ? 'dcf-form-item ' + child.props.className : 'dcf-form-item')\"\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n [formRoot]=\"formGroup\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n } -->\n }\n @if(initialized) {\n <div class=\"dcf-buttons-grid 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 }\n\n </form>\n} @else {\n <section [class]=\"'dcf-grid dcf-grid-small dcf-child-width-1-1 dcf-form-grid ' + operation\" #component [id]=\"uid\">\n @if(!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n @for (child of children; track $index) {\n <div [class]=\"(child?.props?.className ? 'dcf-form-item ' + child.props.className : 'dcf-form-item')\">\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n </div>\n }\n }\n </section>\n <section [class]=\"'dcf-buttons-grid 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 </section>\n}\n\n", styles: [".dcf-buttons-grid{margin-top:.5rem;margin-bottom:0}.dcf-buttons-grid.read{margin-bottom:1rem}@media (min-width: 577px){.dcf-buttons-grid.dcf-flex{flex-direction:row-reverse}}@media (max-width: 576px){.dcf-buttons-grid.dcf-flex>div{width:100%}.dcf-buttons-grid.dcf-flex>div+div{margin-top:1rem}.dcf-buttons-grid.dcf-flex ion-button{width:100%}}.dcf-form-grid{padding:1rem}.dcf-form-grid .dcf-form-item{margin-bottom:1.8rem;margin-top:0!important}.dcf-form-grid.read .dcf-form-item{margin-bottom:1rem}::ng-deep ngx-decaf-stepped-form .dcf-input-item{margin-bottom:1.8rem;margin-top:0!important}::ng-deep ngx-decaf-fieldset .dcf-input-item{margin-bottom:1.8rem;margin-top:0!important}\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: LayoutComponent, selector: "ngx-decaf-layout", inputs: ["gap", "breakpoint", "grid", "match"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { 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"] }] }); }
4719
5105
  };
4720
5106
  CrudFormComponent = __decorate([
4721
5107
  Dynamic(),
@@ -4723,7 +5109,7 @@ CrudFormComponent = __decorate([
4723
5109
  ], CrudFormComponent);
4724
5110
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFormComponent, decorators: [{
4725
5111
  type: Component,
4726
- args: [{ standalone: true, selector: 'ngx-decaf-crud-form', imports: [ReactiveFormsModule, LayoutComponent, ComponentRendererComponent, IonButton, IonIcon], host: { '[attr.id]': 'uid' }, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form\n [class]=\"'dcf-grid dcf-grid-small dcf-child-width-1-1 dcf-form-grid ' + operation\" #component\n [id]=\"rendererId\"\n [formGroup]=\"formGroup\"\n (submit)=\"submit($event)\"\n novalidate\n [target]=\"target\">\n @if(!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n <ngx-decaf-layout\n [children]=\"children || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [initializeProps]=\"false\"\n [rows]=\"rows\"\n [cols]=\"cols\" />\n\n <!-- @for (child of children; track $index) {\n <ngx-decaf-component-renderer\n [class]=\"(child?.props?.className ? 'dcf-form-item ' + child.props.className : 'dcf-form-item')\"\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n [formRoot]=\"formGroup\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n } -->\n }\n <div class=\"dcf-buttons-grid 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 <section [class]=\"'dcf-grid dcf-grid-small dcf-child-width-1-1 dcf-form-grid ' + operation\" #component [id]=\"uid\">\n @if(!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n @for (child of children; track $index) {\n <div [class]=\"(child?.props?.className ? 'dcf-form-item ' + child.props.className : 'dcf-form-item')\">\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n </div>\n }\n }\n </section>\n <section [class]=\"'dcf-buttons-grid 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 </section>\n}\n\n", styles: [".dcf-buttons-grid{margin-top:.5rem;margin-bottom:0}.dcf-buttons-grid.read{margin-bottom:1rem}@media (min-width: 577px){.dcf-buttons-grid.dcf-flex{flex-direction:row-reverse}}@media (max-width: 576px){.dcf-buttons-grid.dcf-flex>div{width:100%}.dcf-buttons-grid.dcf-flex>div+div{margin-top:1rem}.dcf-buttons-grid.dcf-flex ion-button{width:100%}}.dcf-form-grid{padding:1rem}.dcf-form-grid .dcf-form-item{margin-bottom:1.8rem;margin-top:0!important}.dcf-form-grid.read .dcf-form-item{margin-bottom:1rem}::ng-deep ngx-decaf-stepped-form .dcf-input-item{margin-bottom:1.8rem;margin-top:0!important}::ng-deep ngx-decaf-fieldset .dcf-input-item{margin-bottom:1.8rem;margin-top:0!important}\n"] }]
5112
+ args: [{ standalone: true, selector: 'ngx-decaf-crud-form', imports: [ReactiveFormsModule, LayoutComponent, ComponentRendererComponent, IonButton, IonIcon], host: { '[attr.id]': 'uid' }, template: "@if(operation !== 'read' && operation !== 'delete') {\n <form\n [class]=\"'dcf-form-grid ' + operation\" #component\n [id]=\"rendererId\"\n [formGroup]=\"formGroup\"\n (submit)=\"submit($event)\"\n novalidate\n [target]=\"target\">\n @if(!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n <ngx-decaf-layout\n [children]=\"children || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [rows]=\"rows\"\n [cols]=\"cols\" />\n\n <!-- @for (child of children; track $index) {\n <ngx-decaf-component-renderer\n [class]=\"(child?.props?.className ? 'dcf-form-item ' + child.props.className : 'dcf-form-item')\"\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n [formRoot]=\"formGroup\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n } -->\n }\n @if(initialized) {\n <div class=\"dcf-buttons-grid 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 }\n\n </form>\n} @else {\n <section [class]=\"'dcf-grid dcf-grid-small dcf-child-width-1-1 dcf-form-grid ' + operation\" #component [id]=\"uid\">\n @if(!children?.length) {\n <ng-content #formContent></ng-content>\n } @else {\n @for (child of children; track $index) {\n <div [class]=\"(child?.props?.className ? 'dcf-form-item ' + child.props.className : 'dcf-form-item')\">\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n </div>\n }\n }\n </section>\n <section [class]=\"'dcf-buttons-grid 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 </section>\n}\n\n", styles: [".dcf-buttons-grid{margin-top:.5rem;margin-bottom:0}.dcf-buttons-grid.read{margin-bottom:1rem}@media (min-width: 577px){.dcf-buttons-grid.dcf-flex{flex-direction:row-reverse}}@media (max-width: 576px){.dcf-buttons-grid.dcf-flex>div{width:100%}.dcf-buttons-grid.dcf-flex>div+div{margin-top:1rem}.dcf-buttons-grid.dcf-flex ion-button{width:100%}}.dcf-form-grid{padding:1rem}.dcf-form-grid .dcf-form-item{margin-bottom:1.8rem;margin-top:0!important}.dcf-form-grid.read .dcf-form-item{margin-bottom:1rem}::ng-deep ngx-decaf-stepped-form .dcf-input-item{margin-bottom:1.8rem;margin-top:0!important}::ng-deep ngx-decaf-fieldset .dcf-input-item{margin-bottom:1.8rem;margin-top:0!important}\n"] }]
4727
5113
  }], ctorParameters: () => [] });
4728
5114
 
4729
5115
  /**
@@ -4833,10 +5219,10 @@ let EmptyStateComponent = class EmptyStateComponent extends NgxDecafComponentDir
4833
5219
  * and this value should correspond to an available icon name.
4834
5220
  *
4835
5221
  * @type {string}
4836
- * @default "ti-info-square-rounded"
5222
+ * @default "folder-open-outline"
4837
5223
  * @memberOf EmptyStateComponent
4838
5224
  */
4839
- this.icon = "ti-info-square-rounded";
5225
+ this.icon = "folder-open-outline";
4840
5226
  /**
4841
5227
  * @description The size of the displayed icon.
4842
5228
  * @summary Controls the size of the icon shown in the empty state.
@@ -4889,6 +5275,7 @@ let EmptyStateComponent = class EmptyStateComponent extends NgxDecafComponentDir
4889
5275
  */
4890
5276
  this.buttonSize = 'default';
4891
5277
  this.sanitizer = inject(DomSanitizer);
5278
+ this.enableCreationByModelRoute = false;
4892
5279
  addIcons(allIcons);
4893
5280
  }
4894
5281
  /**
@@ -4921,15 +5308,12 @@ let EmptyStateComponent = class EmptyStateComponent extends NgxDecafComponentDir
4921
5308
  async ngOnInit() {
4922
5309
  this.initialize();
4923
5310
  this.showIcon = stringToBoolean(this.showIcon);
4924
- // if(this.translatable) {
4925
- // this.title = generateLocaleFromString(this.locale, this.title);
4926
- // this.subtitle = generateLocaleFromString(this.locale, this.subtitle);
4927
- // this.buttonText = generateLocaleFromString(this.locale, this.buttonText);
4928
- // }
4929
5311
  this.titleColor = `dcf-title color-${this.titleColor}`;
4930
5312
  this.subtitleColor = `dcf-subtitle color-${this.titleColor}`;
4931
5313
  if (this.searchValue)
4932
5314
  this.searchSubtitle = await this.getSearchSubtitle(this.subtitle);
5315
+ if (!this.buttonLink && this.model && this.route)
5316
+ this.enableCreationByModelRoute = true;
4933
5317
  }
4934
5318
  /**
4935
5319
  * @description Handles click events on the action button.
@@ -5001,7 +5385,7 @@ let EmptyStateComponent = class EmptyStateComponent extends NgxDecafComponentDir
5001
5385
  return this.sanitizer.bypassSecurityTrustHtml(result);
5002
5386
  }
5003
5387
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: EmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5004
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: EmptyStateComponent, isStandalone: true, selector: "ngx-decaf-empty-state", inputs: { title: "title", titleColor: "titleColor", subtitle: "subtitle", subtitleColor: "subtitleColor", showIcon: "showIcon", icon: "icon", iconSize: "iconSize", iconColor: "iconColor", buttonLink: "buttonLink", buttonText: "buttonText", buttonFill: "buttonFill", buttonColor: "buttonColor", buttonSize: "buttonSize", searchValue: "searchValue" }, usesInheritance: true, ngImport: i0, template: "\n<ion-card [id]=\"uid\" [class]=\"className\">\n <ion-card-content>\n @if(icon && showIcon) {\n <div class=\"dcf-icon-container\">\n <ion-icon\n name=\"alert-circle-outline\"\n size=\"large\"\n color=\"danger\"\n />\n </div>\n }\n @if(title) {\n <h5 [class]=\"titleColor\" [innerHTML]=\"title\"></h5>\n }\n @if(subtitle) {\n @if(!searchValue) {\n <p [class]=\"subtitleColor\" [innerHTML]=\"subtitle\"></p>\n } @else {\n <p [class]=\"subtitleColor\" [innerHTML]=\"searchSubtitle\"></p>\n }\n }\n @if(buttonLink && buttonText) {\n <div>\n <ion-button\n [size]=\"buttonSize\"\n [fill]=\"buttonFill\"\n [color]=\"buttonColor\"\n (click)=\"handleClick()\">\n {{ buttonText }}\n </ion-button>\n </div>\n }\n </ion-card-content>\n</ion-card>\n", styles: ["ion-card{text-align:center}ion-card ion-button{margin-top:.75rem}ion-card ion-icon{font-size:2.5rem}ion-card .dcf-icon-container{transform:scale(1.25);opacity:.75;margin-top:1.25rem!important;margin-bottom:.5rem!important}ion-card .dcf-ititle{font-weight:600!important;color:var(--dcf-color-gray-6)!important}ion-card .dcf-isubtitle{font-weight:500!important}\n"], dependencies: [{ kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
5388
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: EmptyStateComponent, isStandalone: true, selector: "ngx-decaf-empty-state", inputs: { title: "title", titleColor: "titleColor", subtitle: "subtitle", subtitleColor: "subtitleColor", showIcon: "showIcon", icon: "icon", iconSize: "iconSize", iconColor: "iconColor", buttonLink: "buttonLink", buttonText: "buttonText", buttonFill: "buttonFill", buttonColor: "buttonColor", buttonSize: "buttonSize", searchValue: "searchValue" }, usesInheritance: true, ngImport: i0, template: "\n<ion-card [id]=\"uid\" [class]=\"className\">\n <ion-card-content>\n @if(icon && showIcon) {\n <div class=\"dcf-icon-container\">\n <ion-icon\n [name]=\"icon\"\n [size]=\"iconSize\"\n [color]=\"iconColor\"\n />\n </div>\n }\n @if(title) {\n <h5 [class]=\"titleColor\" [innerHTML]=\"title\"></h5>\n }\n @if(subtitle) {\n <p [class]=\"subtitleColor\" [innerHTML]=\"searchValue ? searchSubtitle : subtitle\"></p>\n }\n @if(!enableCreationByModelRoute) {\n @if(buttonLink && buttonText) {\n <div>\n <ion-button\n [size]=\"buttonSize\"\n [fill]=\"buttonFill\"\n [color]=\"buttonColor\"\n (click)=\"handleClick()\">\n {{ buttonText }}\n </ion-button>\n </div>\n }\n } @else {\n\n <div>\n <ion-button\n class=\"dcf-margin-top\"\n [size]=\"buttonSize\"\n [fill]=\"buttonFill\"\n [color]=\"buttonColor\"\n (click)=\"changeOperation(OperationKeys.CREATE)\" fill=\"clear\">\n {{ buttonText?.length ? buttonText : locale + '.button_create' | translate }}\n </ion-button>\n </div>\n\n }\n\n </ion-card-content>\n</ion-card>\n", styles: ["ion-card{text-align:center}ion-card ion-button{margin-top:.75rem}ion-card ion-icon{font-size:2.5rem}ion-card .dcf-icon-container{transform:scale(1.25);opacity:.75;margin-top:1.25rem!important;margin-bottom:.5rem!important}ion-card .dcf-ititle{font-weight:600!important;color:var(--dcf-color-gray-6)!important}ion-card .dcf-isubtitle{font-weight:500!important}\n"], dependencies: [{ kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { 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: "pipe", type: TranslatePipe, name: "translate" }] }); }
5005
5389
  };
5006
5390
  EmptyStateComponent = __decorate([
5007
5391
  Dynamic(),
@@ -5012,8 +5396,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
5012
5396
  args: [{ selector: 'ngx-decaf-empty-state', standalone: true, imports: [
5013
5397
  IonCard,
5014
5398
  IonCardContent,
5015
- IonIcon
5016
- ], template: "\n<ion-card [id]=\"uid\" [class]=\"className\">\n <ion-card-content>\n @if(icon && showIcon) {\n <div class=\"dcf-icon-container\">\n <ion-icon\n name=\"alert-circle-outline\"\n size=\"large\"\n color=\"danger\"\n />\n </div>\n }\n @if(title) {\n <h5 [class]=\"titleColor\" [innerHTML]=\"title\"></h5>\n }\n @if(subtitle) {\n @if(!searchValue) {\n <p [class]=\"subtitleColor\" [innerHTML]=\"subtitle\"></p>\n } @else {\n <p [class]=\"subtitleColor\" [innerHTML]=\"searchSubtitle\"></p>\n }\n }\n @if(buttonLink && buttonText) {\n <div>\n <ion-button\n [size]=\"buttonSize\"\n [fill]=\"buttonFill\"\n [color]=\"buttonColor\"\n (click)=\"handleClick()\">\n {{ buttonText }}\n </ion-button>\n </div>\n }\n </ion-card-content>\n</ion-card>\n", styles: ["ion-card{text-align:center}ion-card ion-button{margin-top:.75rem}ion-card ion-icon{font-size:2.5rem}ion-card .dcf-icon-container{transform:scale(1.25);opacity:.75;margin-top:1.25rem!important;margin-bottom:.5rem!important}ion-card .dcf-ititle{font-weight:600!important;color:var(--dcf-color-gray-6)!important}ion-card .dcf-isubtitle{font-weight:500!important}\n"] }]
5399
+ IonIcon,
5400
+ TranslatePipe,
5401
+ IonButton
5402
+ ], template: "\n<ion-card [id]=\"uid\" [class]=\"className\">\n <ion-card-content>\n @if(icon && showIcon) {\n <div class=\"dcf-icon-container\">\n <ion-icon\n [name]=\"icon\"\n [size]=\"iconSize\"\n [color]=\"iconColor\"\n />\n </div>\n }\n @if(title) {\n <h5 [class]=\"titleColor\" [innerHTML]=\"title\"></h5>\n }\n @if(subtitle) {\n <p [class]=\"subtitleColor\" [innerHTML]=\"searchValue ? searchSubtitle : subtitle\"></p>\n }\n @if(!enableCreationByModelRoute) {\n @if(buttonLink && buttonText) {\n <div>\n <ion-button\n [size]=\"buttonSize\"\n [fill]=\"buttonFill\"\n [color]=\"buttonColor\"\n (click)=\"handleClick()\">\n {{ buttonText }}\n </ion-button>\n </div>\n }\n } @else {\n\n <div>\n <ion-button\n class=\"dcf-margin-top\"\n [size]=\"buttonSize\"\n [fill]=\"buttonFill\"\n [color]=\"buttonColor\"\n (click)=\"changeOperation(OperationKeys.CREATE)\" fill=\"clear\">\n {{ buttonText?.length ? buttonText : locale + '.button_create' | translate }}\n </ion-button>\n </div>\n\n }\n\n </ion-card-content>\n</ion-card>\n", styles: ["ion-card{text-align:center}ion-card ion-button{margin-top:.75rem}ion-card ion-icon{font-size:2.5rem}ion-card .dcf-icon-container{transform:scale(1.25);opacity:.75;margin-top:1.25rem!important;margin-bottom:.5rem!important}ion-card .dcf-ititle{font-weight:600!important;color:var(--dcf-color-gray-6)!important}ion-card .dcf-isubtitle{font-weight:500!important}\n"] }]
5017
5403
  }], ctorParameters: () => [], propDecorators: { title: [{
5018
5404
  type: Input
5019
5405
  }], titleColor: [{
@@ -5244,11 +5630,6 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
5244
5630
  return child;
5245
5631
  });
5246
5632
  }
5247
- // if(this.model) {
5248
- // this._repository = getModelRepository(this.model);
5249
- // console.log(this._repository);
5250
- // }
5251
- // console.log(this._repository);
5252
5633
  this.initialized = true;
5253
5634
  }
5254
5635
  /**
@@ -5346,13 +5727,8 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
5346
5727
  async handleCreateItem(event) {
5347
5728
  if (event && event instanceof CustomEvent)
5348
5729
  event.stopImmediatePropagation();
5349
- // if(this.updatingItem)
5350
- // return this.handleUpdateItem(this.updatingItem.index, true);
5351
5730
  const action = this.updatingItem ? OperationKeys.UPDATE : OperationKeys.CREATE;
5352
5731
  const formGroup = this.activeFormGroup;
5353
- // currentGroup.updateValueAndValidity();
5354
- // console.log((currentGroup.parent as FormArray).value);
5355
- const parent = formGroup.parent;
5356
5732
  const isValid = NgxDecafFormService.validateFields(formGroup);
5357
5733
  // must pass correct pk here
5358
5734
  const isUnique = NgxDecafFormService.isUniqueOnGroup(formGroup, action, action === OperationKeys.UPDATE ? this.updatingItem?.index : undefined);
@@ -5371,68 +5747,13 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
5371
5747
  value?.[this.pk] || undefined : value;
5372
5748
  }
5373
5749
  }
5374
- // windowEventEmitter(EventConstants.FIELDSET_ADD_GROUP, {
5375
- // formGroup: this.updatingItem ? this.updatingItem : this.formGroup
5376
- // });
5377
- // const formParent = this.formGroup as FormArray;
5378
- // const index = formParent.length - 1;
5379
- // const formGroup = formParent.at(index) as FormGroup;
5380
- // console.log(formGroup.errors);
5381
- // const isValid = NgxDecafFormService.validateFields(this.formGroup as FormGroup);
5382
- // const isUnique = NgxDecafFormService.isUniqueOnGroup(formGroup, index, OperationKeys.CREATE);
5383
- // const value = formGroup.value;
5384
- // console.log(isValid, isUnique);
5385
- // this.changeDetectorRef.detectChanges();
5386
- // if(!Object.keys(this.mapper).length)
5387
- // this.mapper = this.getMapper(value as KeyValue);
5388
- // if(isValid && isUnique) {
5389
- // this.isUniqueError = undefined;
5390
- // this.buttonLabel = this.translateService.instant(this.locale + '.add');
5391
- // this.setValue();
5392
- // // const fb = NgxDecafFormService.addGroupToParent((this.formGroup as FormArray).root as FormGroup, this.childOf as string, formParent.length).parent as FormArray;
5393
- // } else {
5394
- // this.isUniqueError = typeof value === 'object' ? (value as KeyValue)?.[this.pk] || undefined : value;
5395
- // }
5396
- // console.log(this.formGroup);
5397
- // if(event && event instanceof CustomEvent) {
5398
- // event.stopImmediatePropagation();
5399
- // const {formGroup, value, isValid} = event.detail;
5400
- // this.formGroup = formGroup as FormArray;
5401
- // if(!this.mapper)
5402
- // this.mapper = this.getMapper(value as KeyValue);
5403
- // if(isValid ){
5404
- // this.isUniqueError = undefined;
5405
- // this.buttonLabel = this.translateService.instant(this.locale + '.add');
5406
- // this.setValue();
5407
- // } else {
5408
- // this.isUniqueError = (value as KeyValue)?.[this.pk] || undefined;
5409
- // }
5410
- // } else {
5411
- // windowEventEmitter(EventConstants.FIELDSET_ADD_GROUP, {
5412
- // component: this.component.nativeElement,
5413
- // index: this.updatingItem ? this.updatingItem.index : this.value?.length,
5414
- // parent: this.childOf,
5415
- // operation: !this.updatingItem ? OperationKeys.CREATE : OperationKeys.UPDATE
5416
- // });
5417
- // }
5418
5750
  }
5419
- handleUpdateItem(index, save = false) {
5751
+ handleUpdateItem(index) {
5420
5752
  const formGroup = this.getFormArrayIndex(index);
5421
5753
  if (formGroup) {
5422
5754
  this.updatingItem = Object.assign({}, formGroup.value || {}, { index });
5423
5755
  this.activeFormGroupIndex = index;
5424
5756
  }
5425
- // this.changeDetectorRef.detectChanges();
5426
- // const item = this.formGroup.controls.find(control => `${control.get(this.pk)?.value}`.toLowerCase() === cleanSpaces(`${value}`, true)) as FormControl;
5427
- // if(item) {
5428
- // this.buttonLabel = this.translateService.instant(this.locale + '.update');
5429
- // this.updatingItem = Object.assign({}, item.value || {}, {index});
5430
- // windowEventEmitter(EventConstants.FIELDSET_UPDATE_GROUP, {
5431
- // parent: this.childOf,
5432
- // component: this.component.nativeElement,
5433
- // index: index
5434
- // });
5435
- // }
5436
5757
  }
5437
5758
  /**
5438
5759
  * @description Cancels the update mode and resets the UI state.
@@ -5477,23 +5798,6 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
5477
5798
  this.setValue();
5478
5799
  if (this.activeFormGroupIndex > 0)
5479
5800
  this.activeFormGroupIndex = this.activeFormGroupIndex - 1;
5480
- // if(event && event instanceof CustomEvent) {
5481
- // event.stopImmediatePropagation();
5482
- // return this.setValue();
5483
- // }
5484
- // const formArray = this.formGroup as FormArray;
5485
- // const arrayLength = formArray.length;
5486
- // for (let index = arrayLength - 1; index >= 0; index--) {
5487
- // const group = formArray.at(index) as FormGroup;
5488
- // if (cleanSpaces(group.get(this.pk)?.value) === cleanSpaces(value as string)) {
5489
- // windowEventEmitter(EventConstants.FIELDSET_REMOVE_GROUP, {
5490
- // parent: this.childOf,
5491
- // component: this.component.nativeElement,
5492
- // index,
5493
- // formGroup: group
5494
- // });
5495
- // }
5496
- // }
5497
5801
  }
5498
5802
  /**
5499
5803
  * @description Handles reordering of items within the fieldset list.
@@ -5620,16 +5924,7 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
5620
5924
  index: index + 1
5621
5925
  };
5622
5926
  });
5623
- // const inputContainers = this.component.nativeElement.querySelectorAll('.dcf-input-item');
5624
- // inputContainers.forEach((container: HTMLElement) => {
5625
- // const input = container.querySelector('input, ion-input, ion-textarea, textarea') as HTMLInputElement | null;
5626
- // if(input)
5627
- // input.value = '';
5628
- // })
5629
5927
  this.updatingItem = undefined;
5630
- // console.log(this.isUniqueError);
5631
- // console.log(this.mapper);
5632
- // console.log(this.items);
5633
5928
  }
5634
5929
  /**
5635
5930
  * @description Automatically configures the field mapping based on the value structure.
@@ -5661,7 +5956,7 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
5661
5956
  return this.mapper;
5662
5957
  }
5663
5958
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5664
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: FieldsetComponent, isStandalone: true, selector: "ngx-decaf-fieldset", inputs: { formControl: "formControl", collapsable: "collapsable", page: "page", customTypes: "customTypes", title: "title", description: "description", multiple: "multiple", value: "value", borders: "borders" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "accordionComponent", first: true, predicate: ["accordionComponent"], descendants: true }], usesInheritance: true, ngImport: i0, template: "\n\n<fieldset\n (fieldsetAddGroupEvent)=\"handleCreateItem($event)\"\n [class]=\"'dcf-fieldset ' + operation\"\n [class.remove-border]=\"!borders\"\n [class.open]=\"isOpen\"\n #component>\n <ion-accordion-group class=\"dcf-width-1-1\" [class.open]=\"isOpen\" [class.hasValidationErrors]=\"hasValidationErrors\" [value]=\"operation === 'read' ? 'open' : ''\" #accordionComponent>\n <ion-accordion value=\"open\">\n <ion-item [class.disable-collapse]=\"!collapsable\" slot=\"header\" [button]=\"collapsable\" (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($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($index)\">\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 <ng-content select=\"[slot=content]\"></ng-content>\n\n @if (this.children?.length) {\n @for (child of children; track $index) {\n <ngx-decaf-component-renderer\n [class]=\"(child?.props?.className ? ' ' + child.props.className : '')\"\n [tag]=\"child?.tag\"\n [parentComponent]=\"formGroup || parentComponent\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n }\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 @if (items.length < max || !max) {\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 </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}@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}ion-accordion ion-item[slot=header] legend{color:var(--dcf-color-gray-7)}}@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{border:1px solid var(--dcf-color-gray-6)}}.dcf-fieldset{padding-top:1rem;padding-bottom:1rem;background:var(--dcf-card-background);border-radius:6px}.dcf-fieldset.remove-border{border:none!important;padding:0!important;margin:0!important}.dcf-fieldset.open ion-accordion{margin-bottom:1rem}.dcf-fieldset.open ion-accordion.accordion-collapsing,.dcf-fieldset.open ion-accordion.accordion-collapsed{margin-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}.dcf-fieldset ion-accordion{background:var(--dcf-card-background)}.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}.dcf-fieldset ion-accordion [slot=content]{padding-top:1.5rem!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-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: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { 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"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
5959
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: FieldsetComponent, isStandalone: true, selector: "ngx-decaf-fieldset", inputs: { formControl: "formControl", collapsable: "collapsable", page: "page", customTypes: "customTypes", title: "title", description: "description", multiple: "multiple", value: "value", borders: "borders" }, viewQueries: [{ propertyName: "accordionComponent", first: true, predicate: ["accordionComponent"], descendants: true }], usesInheritance: true, ngImport: i0, template: "\n\n<fieldset\n (fieldsetAddGroupEvent)=\"handleCreateItem($event)\"\n [class]=\"'dcf-fieldset ' + operation\"\n [class.remove-border]=\"!borders\"\n [class.open]=\"isOpen\"\n #component>\n <ion-accordion-group class=\"dcf-width-1-1\" [class.open]=\"isOpen\" [class.hasValidationErrors]=\"hasValidationErrors\" [value]=\"operation === 'read' ? 'open' : ''\" #accordionComponent>\n <ion-accordion value=\"open\">\n <ion-item [class.disable-collapse]=\"!collapsable\" slot=\"header\" [button]=\"collapsable\" (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($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($index)\">\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 @if(children?.length) {\n <ngx-decaf-layout\n gap=\"small\"\n [children]=\"children || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n [match]=\"false\"\n\n [rows]=\"rows\"\n [cols]=\"cols\"\n />\n }\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 @if (items.length < max || !max) {\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 </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}@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}ion-accordion ion-item[slot=header] legend{color:var(--dcf-color-gray-7)}}@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{border:1px solid var(--dcf-color-gray-6)}}.dcf-fieldset{padding-top:1rem;padding-bottom:1rem;background:var(--dcf-card-background);border-radius:6px}.dcf-fieldset.remove-border{border:none!important;padding:0!important;margin:0!important}.dcf-fieldset.open ion-accordion{margin-bottom:1rem}.dcf-fieldset.open ion-accordion.accordion-collapsing,.dcf-fieldset.open ion-accordion.accordion-collapsed{margin-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}.dcf-fieldset ion-accordion{background:var(--dcf-card-background)}.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}.dcf-fieldset ion-accordion [slot=content]{padding-top:1.5rem!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-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: "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"] }, { kind: "component", type: LayoutComponent, selector: "ngx-decaf-layout", inputs: ["gap", "breakpoint", "grid", "match"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
5665
5960
  };
5666
5961
  FieldsetComponent = __decorate([
5667
5962
  Dynamic(),
@@ -5671,7 +5966,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
5671
5966
  type: Component,
5672
5967
  args: [{ standalone: true, selector: 'ngx-decaf-fieldset', schemas: [], imports: [
5673
5968
  TranslatePipe,
5674
- ComponentRendererComponent,
5675
5969
  ReactiveFormsModule,
5676
5970
  IonAccordionGroup,
5677
5971
  IonAccordion,
@@ -5683,7 +5977,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
5683
5977
  IonReorderGroup,
5684
5978
  IonButton,
5685
5979
  IonIcon,
5686
- ], host: { '[attr.id]': 'uid' }, template: "\n\n<fieldset\n (fieldsetAddGroupEvent)=\"handleCreateItem($event)\"\n [class]=\"'dcf-fieldset ' + operation\"\n [class.remove-border]=\"!borders\"\n [class.open]=\"isOpen\"\n #component>\n <ion-accordion-group class=\"dcf-width-1-1\" [class.open]=\"isOpen\" [class.hasValidationErrors]=\"hasValidationErrors\" [value]=\"operation === 'read' ? 'open' : ''\" #accordionComponent>\n <ion-accordion value=\"open\">\n <ion-item [class.disable-collapse]=\"!collapsable\" slot=\"header\" [button]=\"collapsable\" (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($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($index)\">\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 <ng-content select=\"[slot=content]\"></ng-content>\n\n @if (this.children?.length) {\n @for (child of children; track $index) {\n <ngx-decaf-component-renderer\n [class]=\"(child?.props?.className ? ' ' + child.props.className : '')\"\n [tag]=\"child?.tag\"\n [parentComponent]=\"formGroup || parentComponent\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n }\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 @if (items.length < max || !max) {\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 </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}@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}ion-accordion ion-item[slot=header] legend{color:var(--dcf-color-gray-7)}}@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{border:1px solid var(--dcf-color-gray-6)}}.dcf-fieldset{padding-top:1rem;padding-bottom:1rem;background:var(--dcf-card-background);border-radius:6px}.dcf-fieldset.remove-border{border:none!important;padding:0!important;margin:0!important}.dcf-fieldset.open ion-accordion{margin-bottom:1rem}.dcf-fieldset.open ion-accordion.accordion-collapsing,.dcf-fieldset.open ion-accordion.accordion-collapsed{margin-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}.dcf-fieldset ion-accordion{background:var(--dcf-card-background)}.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}.dcf-fieldset ion-accordion [slot=content]{padding-top:1.5rem!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-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"] }]
5980
+ LayoutComponent
5981
+ ], template: "\n\n<fieldset\n (fieldsetAddGroupEvent)=\"handleCreateItem($event)\"\n [class]=\"'dcf-fieldset ' + operation\"\n [class.remove-border]=\"!borders\"\n [class.open]=\"isOpen\"\n #component>\n <ion-accordion-group class=\"dcf-width-1-1\" [class.open]=\"isOpen\" [class.hasValidationErrors]=\"hasValidationErrors\" [value]=\"operation === 'read' ? 'open' : ''\" #accordionComponent>\n <ion-accordion value=\"open\">\n <ion-item [class.disable-collapse]=\"!collapsable\" slot=\"header\" [button]=\"collapsable\" (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($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($index)\">\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 @if(children?.length) {\n <ngx-decaf-layout\n gap=\"small\"\n [children]=\"children || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n [match]=\"false\"\n\n [rows]=\"rows\"\n [cols]=\"cols\"\n />\n }\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 @if (items.length < max || !max) {\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 </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}@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}ion-accordion ion-item[slot=header] legend{color:var(--dcf-color-gray-7)}}@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{border:1px solid var(--dcf-color-gray-6)}}.dcf-fieldset{padding-top:1rem;padding-bottom:1rem;background:var(--dcf-card-background);border-radius:6px}.dcf-fieldset.remove-border{border:none!important;padding:0!important;margin:0!important}.dcf-fieldset.open ion-accordion{margin-bottom:1rem}.dcf-fieldset.open ion-accordion.accordion-collapsing,.dcf-fieldset.open ion-accordion.accordion-collapsed{margin-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}.dcf-fieldset ion-accordion{background:var(--dcf-card-background)}.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}.dcf-fieldset ion-accordion [slot=content]{padding-top:1.5rem!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-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"] }]
5687
5982
  }], ctorParameters: () => [], propDecorators: { accordionComponent: [{
5688
5983
  type: ViewChild,
5689
5984
  args: ['accordionComponent', { static: false }]
@@ -7452,39 +7747,22 @@ let ListComponent = class ListComponent extends NgxDecafComponentDirective {
7452
7747
  */
7453
7748
  this.disableSort = false;
7454
7749
  /**
7455
- * @description Icon to display when the list is empty.
7456
- * @summary Specifies the icon shown in the empty state when no data is available.
7457
- * This can be any icon name supported by the application's icon system.
7458
- *
7459
- * @type {string | undefined}
7460
- * @default 'ti-database-exclamation'
7461
- * @memberOf ListComponent
7462
- */
7463
- this.emptyIcon = 'ti-database-exclamation';
7464
- /**
7465
- * @description Configuration for the empty state display.
7466
- * @summary Customizes how the empty state is displayed when no data is available.
7467
- * This includes the title, subtitle, button text, icon, and navigation link.
7468
- *
7469
- * @type {Partial<IListEmptyResult>}
7470
- * @default {
7471
- * title: 'empty.title',
7472
- * subtitle: 'empty.subtitle',
7473
- * showButton: false,
7474
- * icon: 'alert-circle-outline',
7475
- * buttonText: 'locale.empty.button',
7476
- * link: ''
7477
- * }
7478
- * @memberOf ListComponent
7479
- */
7480
- this.empty = {
7481
- title: 'empty.title',
7482
- subtitle: 'empty.subtitle',
7483
- showButton: false,
7484
- icon: 'alert-circle-outline',
7485
- buttonText: 'locale.empty.button',
7486
- link: ''
7487
- };
7750
+ * @description Configuration for the empty state display.
7751
+ * @summary Customizes how the empty state is displayed when no data is available.
7752
+ * This includes the title, subtitle, button text, icon, and navigation link.
7753
+ *
7754
+ * @type {Partial<IListEmptyOptions>}
7755
+ * @default {
7756
+ * title: 'empty.title',
7757
+ * subtitle: 'empty.subtitle',
7758
+ * showButton: false,
7759
+ * icon: 'alert-circle-outline',
7760
+ * buttonText: 'locale.empty.button',
7761
+ * link: ''
7762
+ * }
7763
+ * @memberOf ListComponent
7764
+ */
7765
+ this.empty = {};
7488
7766
  /**
7489
7767
  * @description The current page number in paginated mode.
7490
7768
  * @summary Tracks which page is currently being displayed when the component
@@ -7516,6 +7794,7 @@ let ListComponent = class ListComponent extends NgxDecafComponentDirective {
7516
7794
  * @memberOf ListComponent
7517
7795
  */
7518
7796
  this.skeletonData = new Array(2);
7797
+ this.searching = false;
7519
7798
  /**
7520
7799
  * @description The last page number that was displayed.
7521
7800
  * @summary Keeps track of the previously displayed page number, which is useful
@@ -7608,9 +7887,10 @@ let ListComponent = class ListComponent extends NgxDecafComponentDirective {
7608
7887
  this.disableSort = stringToBoolean(this.disableSort);
7609
7888
  if (typeof this.item?.['tag'] === 'boolean' && this.item?.['tag'] === true)
7610
7889
  this.item['tag'] = ComponentsTagNames.LIST_ITEM;
7890
+ this.empty = Object.assign({}, DefaultListEmptyOptions, this.empty);
7611
7891
  await this.refresh();
7612
- if (this.operations.includes(OperationKeys.CREATE) && this.route)
7613
- this.empty.link = `${this.route}/${OperationKeys.CREATE}`;
7892
+ // if(this.operations.includes(OperationKeys.CREATE) && this.route)
7893
+ // this.empty.link = `${this.route}/${OperationKeys.CREATE}`;
7614
7894
  if (!this.initialized)
7615
7895
  return this.parseProps(this);
7616
7896
  this.initialized = true;
@@ -7781,6 +8061,7 @@ let ListComponent = class ListComponent extends NgxDecafComponentDirective {
7781
8061
  * @memberOf ListComponent
7782
8062
  */
7783
8063
  async handleSearch(value) {
8064
+ this.searching = value !== undefined;
7784
8065
  if (this.type === ListComponentsTypes.INFINITE) {
7785
8066
  this.loadMoreData = false;
7786
8067
  if (value === undefined) {
@@ -8072,8 +8353,10 @@ let ListComponent = class ListComponent extends NgxDecafComponentDirective {
8072
8353
  if (!this.indexes)
8073
8354
  this.indexes = (Object.values(this.mapper) || [this.pk]);
8074
8355
  const condition = this.parseConditions(this.searchValue);
8356
+ this.changeDetectorRef.detectChanges();
8075
8357
  request = await this.parseResult(await repo.query(condition, (this.sortBy || this.pk), this.sortDirection));
8076
8358
  data = [];
8359
+ this.changeDetectorRef.detectChanges();
8077
8360
  }
8078
8361
  data = this.type === ListComponentsTypes.INFINITE ? [...(data).concat(request)] : [...request];
8079
8362
  }
@@ -8310,8 +8593,14 @@ let ListComponent = class ListComponent extends NgxDecafComponentDirective {
8310
8593
  return accum;
8311
8594
  }, []);
8312
8595
  }
8596
+ parseSearchValue() {
8597
+ if (typeof this.searchValue === Primitives.STRING)
8598
+ return this.searchValue || "";
8599
+ const searchValue = this.searchValue;
8600
+ return (searchValue?.query).map(item => `${item.index} ${item.condition} ${item.value}`).join(", ");
8601
+ }
8313
8602
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8314
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ListComponent, isStandalone: true, selector: "ngx-decaf-list", inputs: { type: "type", 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: "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", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
8603
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ListComponent, isStandalone: true, selector: "ngx-decaf-list", inputs: { type: "type", 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", 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) {\n @if(model && enableFilter) {\n @if(data?.length || searching) {\n <ngx-decaf-filter\n [model]=\"model\"\n [sortDirection]=\"sortDirection\"\n [disableSort]=\"disableSort\"\n (filterEvent)=\"handleFilter($event)\"\n (searchEvent)=\"handleSearch($event)\"\n />\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(!searching) {\n <ngx-decaf-empty-state\n [title]=\"(locale + '.'+ empty.title) | translate\"\n [model]=\"model\"\n [icon]=\"empty.icon\"\n [subtitle]=\"(locale + '.'+ empty.subtitle) | translate\" />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n [model]=\"model\"\n className=\"empty-search\"\n [translatable]=\"true\"\n [title]=\"locale + '.search.title' | translate\"\n [subtitle]=\"locale + '.search.subtitle' | translate: {'0': parseSearchValue()}\"\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: "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", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
8315
8604
  };
8316
8605
  ListComponent = __decorate([
8317
8606
  Dynamic(),
@@ -8338,7 +8627,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
8338
8627
  EmptyStateComponent,
8339
8628
  FilterComponent,
8340
8629
  ComponentRendererComponent
8341
- ], 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"] }]
8630
+ ], 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) {\n @if(model && enableFilter) {\n @if(data?.length || searching) {\n <ngx-decaf-filter\n [model]=\"model\"\n [sortDirection]=\"sortDirection\"\n [disableSort]=\"disableSort\"\n (filterEvent)=\"handleFilter($event)\"\n (searchEvent)=\"handleSearch($event)\"\n />\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(!searching) {\n <ngx-decaf-empty-state\n [title]=\"(locale + '.'+ empty.title) | translate\"\n [model]=\"model\"\n [icon]=\"empty.icon\"\n [subtitle]=\"(locale + '.'+ empty.subtitle) | translate\" />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n [model]=\"model\"\n className=\"empty-search\"\n [translatable]=\"true\"\n [title]=\"locale + '.search.title' | translate\"\n [subtitle]=\"locale + '.search.subtitle' | translate: {'0': parseSearchValue()}\"\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"] }]
8342
8631
  }], ctorParameters: () => [], propDecorators: { type: [{
8343
8632
  type: Input
8344
8633
  }], showSearchbar: [{
@@ -8375,8 +8664,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
8375
8664
  type: Input
8376
8665
  }], disableSort: [{
8377
8666
  type: Input
8378
- }], emptyIcon: [{
8379
- type: Input
8380
8667
  }], empty: [{
8381
8668
  type: Input
8382
8669
  }], refreshEvent: [{
@@ -8891,17 +9178,6 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxParentComponent
8891
9178
  * @memberOf SteppedFormComponent
8892
9179
  */
8893
9180
  this.pagesArray = [];
8894
- /**
8895
- * @description Angular Location service.
8896
- * @summary Injected service that provides access to the browser's URL and history.
8897
- * This service is used for interacting with the browser's history API, allowing
8898
- * for back navigation and URL manipulation outside of Angular's router.
8899
- *
8900
- * @private
8901
- * @type {Location}
8902
- * @memberOf CrudFormComponent
8903
- */
8904
- this.location = inject(Location);
8905
9181
  /**
8906
9182
  * @description Event emitter for form submission.
8907
9183
  * @summary Emits events when the form is submitted, typically on the last page
@@ -8937,12 +9213,16 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxParentComponent
8937
9213
  * @memberOf SteppedFormComponent
8938
9214
  */
8939
9215
  async ngOnInit() {
8940
- console.log(this);
8941
9216
  if (!this.locale)
8942
9217
  this.locale = getLocaleContext("SteppedFormComponent");
8943
9218
  this.activePage = this.startPage;
8944
- if (!this.pageTitles.length)
8945
- this.pageTitles = Array.from({ length: this.pages }, () => ({ title: '', description: '', rendered: this.paginated }));
9219
+ if (typeof this.pages === 'object') {
9220
+ this.pageTitles = this.pages;
9221
+ }
9222
+ else {
9223
+ if (!this.pageTitles.length)
9224
+ this.pageTitles = Array.from({ length: this.pages }, () => ({ title: '', description: '' }));
9225
+ }
8946
9226
  this.pages = this.pageTitles.length;
8947
9227
  if (this.paginated) {
8948
9228
  this.children = [...this.children.map((c) => {
@@ -8968,6 +9248,7 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxParentComponent
8968
9248
  });
8969
9249
  this.activeFormGroup = this.formGroup;
8970
9250
  }
9251
+ this.initialized = true;
8971
9252
  }
8972
9253
  /**
8973
9254
  * @description Cleanup method called when the component is destroyed.
@@ -9086,9 +9367,7 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxParentComponent
9086
9367
  this.timerSubscription = timer(10).subscribe(() => this.activeChildren = this.children.filter(c => c.props?.['page'] === page));
9087
9368
  }
9088
9369
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: SteppedFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9089
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: SteppedFormComponent, isStandalone: true, selector: "ngx-decaf-stepped-form", inputs: { children: "children", paginated: "paginated", pages: "pages", pageTitles: "pageTitles", operation: "operation", startPage: "startPage", formGroup: "formGroup" }, outputs: { submitEvent: "submitEvent" }, host: { properties: { "attr.id": "uid" } }, usesInheritance: true, ngImport: i0, template: "<form class=\"dcf-steped-form\" [class.paginated]=\"paginated\" novalidate>\n @if(paginated) {\n <div class=\"dcf-page-steps\">\n <div class=\"dcf-grid dcf-grid-collapse skip\">\n @for(page of pageTitles; track $index;) {\n <div class=\"dcf-flex dcf-flex-middle\">\n <div class=\"dcf-step\" [class.dcf-active]=\"activePage === $index + 1\" [class.dcf-passed]=\"($index + 1) < activePage\">{{ $index + 1 }}</div>\n @if((page?.title || page?.description)) {\n <div class=\"dcf-information dcf-visible@s\">\n @if(page?.title) {\n <div class=\"dcf-title\">{{ page?.title | translate }}</div>\n }\n @if(page?.description) {\n <div class=\"dcf-description\">{{ page?.description | translate }}</div>\n }\n <div class=\"dcf-separator\"></div>\n </div>\n }\n @if($index < pageTitles.length - 1) {\n <div class=\"dcf-arrow-container\">\n <svg width=\"8\" height=\"12\" viewBox=\"0 0 8 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M1.5 1L6.5 6L1.5 11\" />\n </svg>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if(pageTitles[activePage - 1]?.title || pageTitles[activePage - 1]?.description) {\n <div class=\"dcf-current-step\">\n <div>\n @if(pageTitles[activePage - 1]?.title) {\n <div class=\"dcf-title\">{{ pageTitles[activePage - 1]?.title | translate }}</div>\n }\n @if(pageTitles[activePage - 1]?.description) {\n <div class=\"dcf-description\">{{ pageTitles[activePage - 1]?.description | translate }}</div>\n }\n </div>\n </div>\n }\n\n @if(formGroup) {\n <!-- <ngx-decaf-layout\n [children]=\"children || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [initializeProps]=\"false\"\n [rows]=\"rows ?? 1\"\n [cols]=\"cols ?? 1\" /> -->\n @for(child of activeChildren; track $index) {\n <div class=\"dcf-step-form-container\">\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n </div>\n\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 <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 color=\"light\" (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=\"solid\" (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 } @else {\n <div class=\"dcf-single-step\">\n @for (item of children; track $index) {\n <ion-card>\n <ion-card-content>\n @if(item.title || item.description) {\n <div class=\"dcf-information\">\n <div>\n @if(item.title) {\n <div class=\"dcf-title\">{{ item.title | translate }}</div>\n }\n @if(item.description) {\n <div class=\"dcf-description\">{{ item.description | translate }}</div>\n }\n </div>\n </div>\n }\n <div class=\"dcf-grid dcf-grid-small dcf-child-width-1-1\">\n @for (child of item.items; track $index) {\n <ngx-decaf-component-renderer\n [class]=\"'dcf-step-container ' + (child?.props?.className ? child.props.className + ' ' : ' ') + child.tag\"\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n (listenEvent)=\"handleEvent($event)\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </ion-card-content>\n </ion-card>\n }\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-small dcf-flex dcf-flex-right\">\n <div class=\"dcf-width-auto@s dcf-width-1-1\">\n <ion-button color=\"light\" (click)=\"handleBack()\">\n {{locale + '.cancel' | translate}}\n </ion-button>\n </div>\n\n <div class=\"dcf-width-auto@s dcf-width-1-1\">\n <ion-button fill=\"solid\" (click)=\"handleNext(true)\">\n {{locale + '.submit' | translate}}\n </ion-button>\n </div>\n </div>\n </div>\n }\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.paginated{padding:2rem 1rem}.dcf-page-steps{display:flex;justify-content:center;align-items:center;margin-bottom:2rem;overflow-x:auto;flex-wrap:nowrap}.dcf-page-steps .dcf-grid{display:flex!important;flex-wrap:nowrap!important;align-items:center}.dcf-step{height:38px;min-width:38px;width:38px;background:var(--dcf-color-gray-2);margin:0;display:flex;color:var(--dcf-color-gray-7);justify-content:center;align-items:center;font-size:1rem;font-weight:600;border-radius:var(--dcf-border-radius)}.dcf-step.dcf-active{color:var(--dcf-color-light);background:var(--ion-color-primary);box-shadow:0 2px 6px #144c714d}.dcf-step.dcf-passed{color:var(--dcf-color-primary-shade);background:var(--dcf-color-gray-3)}.dcf-information{padding-left:.5rem}.dcf-title{font-size:.875rem;font-weight:600;margin:0;color:var(--dcf-color-gray-7)}.dcf-description{font-size:.8rem;font-weight:500;margin:0;margin-top:.25rem;color:var(--dcf-color-gray-4)}.dcf-arrow-container{display:flex;align-items:center;justify-content:center;padding:0 .5rem;height:38px}.dcf-arrow-container{display:flex;align-items:center;justify-content:center;padding:0 1rem;height:40px}.dcf-arrow-container svg{width:1rem;height:1rem;stroke:var(--dcf-color-gray-8);stroke-opacity:.7;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}ion-button[color=light]{--background: var(--dcf-color-gray-7) !important}.dcf-current-step{margin-top:-.5rem!important;margin-bottom:1.75rem!important;display:flex;align-items:center;justify-content:center}@media (min-width: 639px){.dcf-current-step{display:none}}.dcf-single-step .dcf-information{margin-bottom:1rem}.dcf-single-step .dcf-step-container:not(.ngx-decaf-fieldset){padding-bottom:1.5rem;padding-top:.5rem}.dcf-single-step ion-card{margin-bottom:1.5rem}.dcf-single-step ion-card .dcf-information{margin-top:.75rem}.dcf-step-form-container{margin-bottom:1.75rem!important}::ng-deep ngx-decaf-component-renderer.ngx-decaf-fieldset,::ng-deep ngx-decaf-fieldset{padding-top:0!important;padding-bottom:0!important}::ng-deep ngx-decaf-component-renderer.ngx-decaf-fieldset ion-button,::ng-deep ngx-decaf-fieldset ion-button{margin-top:1rem!important}\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: "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:
9090
- // LayoutComponent,
9091
- ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
9370
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: SteppedFormComponent, isStandalone: true, selector: "ngx-decaf-stepped-form", inputs: { children: "children", paginated: "paginated", pages: "pages", pageTitles: "pageTitles", operation: "operation", startPage: "startPage", formGroup: "formGroup" }, outputs: { submitEvent: "submitEvent" }, usesInheritance: true, ngImport: i0, template: "<form class=\"dcf-steped-form\" [class.paginated]=\"paginated\" novalidate>\n @if(paginated) {\n <div class=\"dcf-page-steps\">\n <div class=\"dcf-grid dcf-grid-collapse skip\">\n @for(page of pageTitles; track $index;) {\n <div class=\"dcf-flex dcf-flex-middle\">\n <div class=\"dcf-step\" [class.dcf-active]=\"activePage === $index + 1\" [class.dcf-passed]=\"($index + 1) < activePage\">{{ $index + 1 }}</div>\n @if((page?.title || page?.description)) {\n <div class=\"dcf-information dcf-visible@s\">\n @if(page?.title) {\n <div class=\"dcf-title\">{{ page?.title | translate }}</div>\n }\n @if(page?.description) {\n <div class=\"dcf-description\">{{ page?.description | translate }}</div>\n }\n <div class=\"dcf-separator\"></div>\n </div>\n }\n @if($index < pageTitles.length - 1) {\n <div class=\"dcf-arrow-container\">\n <svg width=\"8\" height=\"12\" viewBox=\"0 0 8 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M1.5 1L6.5 6L1.5 11\" />\n </svg>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if(pageTitles[activePage - 1]?.title || pageTitles[activePage - 1]?.description) {\n <div class=\"dcf-current-step\">\n <div>\n @if(pageTitles[activePage - 1]?.title) {\n <div class=\"dcf-title\">{{ pageTitles[activePage - 1]?.title | translate }}</div>\n }\n @if(pageTitles[activePage - 1]?.description) {\n <div class=\"dcf-description\">{{ pageTitles[activePage - 1]?.description | translate }}</div>\n }\n </div>\n </div>\n }\n\n @if(initialized && activeChildren?.length) {\n <ngx-decaf-layout\n [children]=\"activeChildren || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [match]=\"false\"\n\n />\n <!-- @for(child of activeChildren; track $index) {\n <div class=\"dcf-step-form-container\">\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n </div>\n\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 <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 color=\"light\" (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=\"solid\" (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 } @else {\n <div class=\"dcf-single-step\">\n @for (item of children; track $index) {\n <ion-card>\n <ion-card-content>\n @if(item.title || item.description) {\n <div class=\"dcf-information\">\n <div>\n @if(item.title) {\n <div class=\"dcf-title\">{{ item.title | translate }}</div>\n }\n @if(item.description) {\n <div class=\"dcf-description\">{{ item.description | translate }}</div>\n }\n </div>\n </div>\n }\n <div>\n @if(initialized && item.items?.length) {\n <div>\n <ngx-decaf-layout\n [children]=\"item.items || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [match]=\"false\"\n\n />\n </div>\n\n }\n </div>\n </ion-card-content>\n </ion-card>\n }\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-small dcf-flex dcf-flex-right\">\n <div class=\"dcf-width-auto@s dcf-width-1-1\">\n <ion-button color=\"light\" (click)=\"handleBack()\">\n {{locale + '.cancel' | translate}}\n </ion-button>\n </div>\n\n <div class=\"dcf-width-auto@s dcf-width-1-1\">\n <ion-button fill=\"solid\" (click)=\"handleNext(true)\">\n {{locale + '.submit' | translate}}\n </ion-button>\n </div>\n </div>\n </div>\n }\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.paginated{padding:2rem 1rem}.dcf-page-steps{display:flex;justify-content:center;align-items:center;margin-bottom:2rem;overflow-x:auto;flex-wrap:nowrap}.dcf-page-steps .dcf-grid{display:flex!important;flex-wrap:nowrap!important;align-items:center}.dcf-step{height:38px;min-width:38px;width:38px;background:var(--dcf-color-gray-2);margin:0;display:flex;color:var(--dcf-color-gray-7);justify-content:center;align-items:center;font-size:1rem;font-weight:600;border-radius:var(--dcf-border-radius)}.dcf-step.dcf-active{color:var(--dcf-color-light);background:var(--ion-color-primary);box-shadow:0 2px 6px #144c714d}.dcf-step.dcf-passed{color:var(--dcf-color-primary-shade);background:var(--dcf-color-gray-3)}.dcf-information{padding-left:.5rem}.dcf-title{font-size:.875rem;font-weight:600;margin:0;color:var(--dcf-color-gray-7)}.dcf-description{font-size:.8rem;font-weight:500;margin:0;margin-top:.25rem;color:var(--dcf-color-gray-4)}.dcf-arrow-container{display:flex;align-items:center;justify-content:center;padding:0 .5rem;height:38px}.dcf-arrow-container{display:flex;align-items:center;justify-content:center;padding:0 1rem;height:40px}.dcf-arrow-container svg{width:1rem;height:1rem;stroke:var(--dcf-color-gray-8);stroke-opacity:.7;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}ion-button[color=light]{--background: var(--dcf-color-gray-7) !important}.dcf-current-step{margin-top:-.5rem!important;margin-bottom:1.75rem!important;display:flex;align-items:center;justify-content:center}@media (min-width: 639px){.dcf-current-step{display:none}}.dcf-single-step .dcf-information{margin-bottom:1rem}.dcf-single-step .dcf-step-container:not(.ngx-decaf-fieldset){padding-bottom:1.5rem;padding-top:.5rem}.dcf-single-step ion-card{margin-bottom:1.5rem}.dcf-single-step ion-card .dcf-information{margin-top:.75rem}.dcf-step-form-container{margin-bottom:1.75rem!important}::ng-deep ngx-decaf-component-renderer.ngx-decaf-fieldset,::ng-deep ngx-decaf-fieldset{padding-top:0!important;padding-bottom:0!important}::ng-deep ngx-decaf-component-renderer.ngx-decaf-fieldset ion-button,::ng-deep ngx-decaf-fieldset ion-button{margin-top:1rem!important}\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: "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: LayoutComponent, selector: "ngx-decaf-layout", inputs: ["gap", "breakpoint", "grid", "match"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
9092
9371
  };
9093
9372
  SteppedFormComponent = __decorate([
9094
9373
  Dynamic(),
@@ -9102,9 +9381,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
9102
9381
  IonSkeletonText,
9103
9382
  IonText,
9104
9383
  IonButton,
9105
- // LayoutComponent,
9106
- ComponentRendererComponent
9107
- ], standalone: true, host: { '[attr.id]': 'uid' }, template: "<form class=\"dcf-steped-form\" [class.paginated]=\"paginated\" novalidate>\n @if(paginated) {\n <div class=\"dcf-page-steps\">\n <div class=\"dcf-grid dcf-grid-collapse skip\">\n @for(page of pageTitles; track $index;) {\n <div class=\"dcf-flex dcf-flex-middle\">\n <div class=\"dcf-step\" [class.dcf-active]=\"activePage === $index + 1\" [class.dcf-passed]=\"($index + 1) < activePage\">{{ $index + 1 }}</div>\n @if((page?.title || page?.description)) {\n <div class=\"dcf-information dcf-visible@s\">\n @if(page?.title) {\n <div class=\"dcf-title\">{{ page?.title | translate }}</div>\n }\n @if(page?.description) {\n <div class=\"dcf-description\">{{ page?.description | translate }}</div>\n }\n <div class=\"dcf-separator\"></div>\n </div>\n }\n @if($index < pageTitles.length - 1) {\n <div class=\"dcf-arrow-container\">\n <svg width=\"8\" height=\"12\" viewBox=\"0 0 8 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M1.5 1L6.5 6L1.5 11\" />\n </svg>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if(pageTitles[activePage - 1]?.title || pageTitles[activePage - 1]?.description) {\n <div class=\"dcf-current-step\">\n <div>\n @if(pageTitles[activePage - 1]?.title) {\n <div class=\"dcf-title\">{{ pageTitles[activePage - 1]?.title | translate }}</div>\n }\n @if(pageTitles[activePage - 1]?.description) {\n <div class=\"dcf-description\">{{ pageTitles[activePage - 1]?.description | translate }}</div>\n }\n </div>\n </div>\n }\n\n @if(formGroup) {\n <!-- <ngx-decaf-layout\n [children]=\"children || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [initializeProps]=\"false\"\n [rows]=\"rows ?? 1\"\n [cols]=\"cols ?? 1\" /> -->\n @for(child of activeChildren; track $index) {\n <div class=\"dcf-step-form-container\">\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n </div>\n\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 <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 color=\"light\" (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=\"solid\" (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 } @else {\n <div class=\"dcf-single-step\">\n @for (item of children; track $index) {\n <ion-card>\n <ion-card-content>\n @if(item.title || item.description) {\n <div class=\"dcf-information\">\n <div>\n @if(item.title) {\n <div class=\"dcf-title\">{{ item.title | translate }}</div>\n }\n @if(item.description) {\n <div class=\"dcf-description\">{{ item.description | translate }}</div>\n }\n </div>\n </div>\n }\n <div class=\"dcf-grid dcf-grid-small dcf-child-width-1-1\">\n @for (child of item.items; track $index) {\n <ngx-decaf-component-renderer\n [class]=\"'dcf-step-container ' + (child?.props?.className ? child.props.className + ' ' : ' ') + child.tag\"\n [tag]=\"child?.tag\"\n [projectable]=\"false\"\n (listenEvent)=\"handleEvent($event)\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </ion-card-content>\n </ion-card>\n }\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-small dcf-flex dcf-flex-right\">\n <div class=\"dcf-width-auto@s dcf-width-1-1\">\n <ion-button color=\"light\" (click)=\"handleBack()\">\n {{locale + '.cancel' | translate}}\n </ion-button>\n </div>\n\n <div class=\"dcf-width-auto@s dcf-width-1-1\">\n <ion-button fill=\"solid\" (click)=\"handleNext(true)\">\n {{locale + '.submit' | translate}}\n </ion-button>\n </div>\n </div>\n </div>\n }\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.paginated{padding:2rem 1rem}.dcf-page-steps{display:flex;justify-content:center;align-items:center;margin-bottom:2rem;overflow-x:auto;flex-wrap:nowrap}.dcf-page-steps .dcf-grid{display:flex!important;flex-wrap:nowrap!important;align-items:center}.dcf-step{height:38px;min-width:38px;width:38px;background:var(--dcf-color-gray-2);margin:0;display:flex;color:var(--dcf-color-gray-7);justify-content:center;align-items:center;font-size:1rem;font-weight:600;border-radius:var(--dcf-border-radius)}.dcf-step.dcf-active{color:var(--dcf-color-light);background:var(--ion-color-primary);box-shadow:0 2px 6px #144c714d}.dcf-step.dcf-passed{color:var(--dcf-color-primary-shade);background:var(--dcf-color-gray-3)}.dcf-information{padding-left:.5rem}.dcf-title{font-size:.875rem;font-weight:600;margin:0;color:var(--dcf-color-gray-7)}.dcf-description{font-size:.8rem;font-weight:500;margin:0;margin-top:.25rem;color:var(--dcf-color-gray-4)}.dcf-arrow-container{display:flex;align-items:center;justify-content:center;padding:0 .5rem;height:38px}.dcf-arrow-container{display:flex;align-items:center;justify-content:center;padding:0 1rem;height:40px}.dcf-arrow-container svg{width:1rem;height:1rem;stroke:var(--dcf-color-gray-8);stroke-opacity:.7;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}ion-button[color=light]{--background: var(--dcf-color-gray-7) !important}.dcf-current-step{margin-top:-.5rem!important;margin-bottom:1.75rem!important;display:flex;align-items:center;justify-content:center}@media (min-width: 639px){.dcf-current-step{display:none}}.dcf-single-step .dcf-information{margin-bottom:1rem}.dcf-single-step .dcf-step-container:not(.ngx-decaf-fieldset){padding-bottom:1.5rem;padding-top:.5rem}.dcf-single-step ion-card{margin-bottom:1.5rem}.dcf-single-step ion-card .dcf-information{margin-top:.75rem}.dcf-step-form-container{margin-bottom:1.75rem!important}::ng-deep ngx-decaf-component-renderer.ngx-decaf-fieldset,::ng-deep ngx-decaf-fieldset{padding-top:0!important;padding-bottom:0!important}::ng-deep ngx-decaf-component-renderer.ngx-decaf-fieldset ion-button,::ng-deep ngx-decaf-fieldset ion-button{margin-top:1rem!important}\n"] }]
9384
+ LayoutComponent
9385
+ ], standalone: true, template: "<form class=\"dcf-steped-form\" [class.paginated]=\"paginated\" novalidate>\n @if(paginated) {\n <div class=\"dcf-page-steps\">\n <div class=\"dcf-grid dcf-grid-collapse skip\">\n @for(page of pageTitles; track $index;) {\n <div class=\"dcf-flex dcf-flex-middle\">\n <div class=\"dcf-step\" [class.dcf-active]=\"activePage === $index + 1\" [class.dcf-passed]=\"($index + 1) < activePage\">{{ $index + 1 }}</div>\n @if((page?.title || page?.description)) {\n <div class=\"dcf-information dcf-visible@s\">\n @if(page?.title) {\n <div class=\"dcf-title\">{{ page?.title | translate }}</div>\n }\n @if(page?.description) {\n <div class=\"dcf-description\">{{ page?.description | translate }}</div>\n }\n <div class=\"dcf-separator\"></div>\n </div>\n }\n @if($index < pageTitles.length - 1) {\n <div class=\"dcf-arrow-container\">\n <svg width=\"8\" height=\"12\" viewBox=\"0 0 8 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M1.5 1L6.5 6L1.5 11\" />\n </svg>\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if(pageTitles[activePage - 1]?.title || pageTitles[activePage - 1]?.description) {\n <div class=\"dcf-current-step\">\n <div>\n @if(pageTitles[activePage - 1]?.title) {\n <div class=\"dcf-title\">{{ pageTitles[activePage - 1]?.title | translate }}</div>\n }\n @if(pageTitles[activePage - 1]?.description) {\n <div class=\"dcf-description\">{{ pageTitles[activePage - 1]?.description | translate }}</div>\n }\n </div>\n </div>\n }\n\n @if(initialized && activeChildren?.length) {\n <ngx-decaf-layout\n [children]=\"activeChildren || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [match]=\"false\"\n\n />\n <!-- @for(child of activeChildren; track $index) {\n <div class=\"dcf-step-form-container\">\n <ngx-decaf-component-renderer\n [tag]=\"child?.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [children]=\"child?.children || []\"\n [globals]=\"{props: child.props}\"\n />\n </div>\n\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 <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 color=\"light\" (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=\"solid\" (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 } @else {\n <div class=\"dcf-single-step\">\n @for (item of children; track $index) {\n <ion-card>\n <ion-card-content>\n @if(item.title || item.description) {\n <div class=\"dcf-information\">\n <div>\n @if(item.title) {\n <div class=\"dcf-title\">{{ item.title | translate }}</div>\n }\n @if(item.description) {\n <div class=\"dcf-description\">{{ item.description | translate }}</div>\n }\n </div>\n </div>\n }\n <div>\n @if(initialized && item.items?.length) {\n <div>\n <ngx-decaf-layout\n [children]=\"item.items || []\"\n [parentComponent]=\"formGroup || parentComponent\"\n gap=\"small\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [match]=\"false\"\n\n />\n </div>\n\n }\n </div>\n </ion-card-content>\n </ion-card>\n }\n <div class=\"dcf-buttons-container dcf-grid dcf-grid-small dcf-flex dcf-flex-right\">\n <div class=\"dcf-width-auto@s dcf-width-1-1\">\n <ion-button color=\"light\" (click)=\"handleBack()\">\n {{locale + '.cancel' | translate}}\n </ion-button>\n </div>\n\n <div class=\"dcf-width-auto@s dcf-width-1-1\">\n <ion-button fill=\"solid\" (click)=\"handleNext(true)\">\n {{locale + '.submit' | translate}}\n </ion-button>\n </div>\n </div>\n </div>\n }\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.paginated{padding:2rem 1rem}.dcf-page-steps{display:flex;justify-content:center;align-items:center;margin-bottom:2rem;overflow-x:auto;flex-wrap:nowrap}.dcf-page-steps .dcf-grid{display:flex!important;flex-wrap:nowrap!important;align-items:center}.dcf-step{height:38px;min-width:38px;width:38px;background:var(--dcf-color-gray-2);margin:0;display:flex;color:var(--dcf-color-gray-7);justify-content:center;align-items:center;font-size:1rem;font-weight:600;border-radius:var(--dcf-border-radius)}.dcf-step.dcf-active{color:var(--dcf-color-light);background:var(--ion-color-primary);box-shadow:0 2px 6px #144c714d}.dcf-step.dcf-passed{color:var(--dcf-color-primary-shade);background:var(--dcf-color-gray-3)}.dcf-information{padding-left:.5rem}.dcf-title{font-size:.875rem;font-weight:600;margin:0;color:var(--dcf-color-gray-7)}.dcf-description{font-size:.8rem;font-weight:500;margin:0;margin-top:.25rem;color:var(--dcf-color-gray-4)}.dcf-arrow-container{display:flex;align-items:center;justify-content:center;padding:0 .5rem;height:38px}.dcf-arrow-container{display:flex;align-items:center;justify-content:center;padding:0 1rem;height:40px}.dcf-arrow-container svg{width:1rem;height:1rem;stroke:var(--dcf-color-gray-8);stroke-opacity:.7;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}ion-button[color=light]{--background: var(--dcf-color-gray-7) !important}.dcf-current-step{margin-top:-.5rem!important;margin-bottom:1.75rem!important;display:flex;align-items:center;justify-content:center}@media (min-width: 639px){.dcf-current-step{display:none}}.dcf-single-step .dcf-information{margin-bottom:1rem}.dcf-single-step .dcf-step-container:not(.ngx-decaf-fieldset){padding-bottom:1.5rem;padding-top:.5rem}.dcf-single-step ion-card{margin-bottom:1.5rem}.dcf-single-step ion-card .dcf-information{margin-top:.75rem}.dcf-step-form-container{margin-bottom:1.75rem!important}::ng-deep ngx-decaf-component-renderer.ngx-decaf-fieldset,::ng-deep ngx-decaf-fieldset{padding-top:0!important;padding-bottom:0!important}::ng-deep ngx-decaf-component-renderer.ngx-decaf-fieldset ion-button,::ng-deep ngx-decaf-fieldset ion-button{margin-top:1rem!important}\n"] }]
9108
9386
  }], ctorParameters: () => [], propDecorators: { children: [{
9109
9387
  type: Input
9110
9388
  }], paginated: [{
@@ -9278,5 +9556,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
9278
9556
  * Generated bundle index. Do not edit.
9279
9557
  */
9280
9558
 
9281
- export { AngularEngineKeys, BaseComponentProps, CPTKN, CollapsableDirective, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_PROVIDER, DB_ADAPTER_PROVIDER_TOKEN, DefaultFormReactiveOptions, Dynamic, DynamicModule, EmptyStateComponent, EventConstants, FieldsetComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, FormConstants, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, I18nParser, LOCALE_ROOT_TOKEN, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, LoggerLevels, ModelRendererComponent, NgxDecafComponentDirective, NgxDecafFormFieldDirective, NgxDecafFormService, NgxEventHandler, NgxPageDirective, NgxRenderingEngine, PaginationComponent, RouteDirections, SearchbarComponent, SteppedFormComponent, cleanSpaces, dataMapper, formatDate, generateRandomValue, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getModelRepository, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, parseToValidDate, provideDbAdapter, provideI18n, provideI18nLoader, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
9559
+ export { AngularEngineKeys, BaseComponentProps, CPTKN, CollapsableDirective, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_PROVIDER, DB_ADAPTER_PROVIDER_TOKEN, DefaultFormReactiveOptions, DefaultListEmptyOptions, Dynamic, DynamicModule, EmptyStateComponent, EventConstants, FieldsetComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, FormConstants, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, I18nParser, LOCALE_ROOT_TOKEN, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, LoggerLevels, ModelRendererComponent, NgxDecafComponentDirective, NgxDecafFormFieldDirective, NgxDecafFormService, NgxEventHandler, NgxPageDirective, NgxRenderingEngine, PaginationComponent, RouteDirections, SearchbarComponent, SteppedFormComponent, cleanSpaces, dataMapper, formatDate, generateRandomValue, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getModelRepository, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, parseToValidDate, provideDbAdapter, provideI18n, provideI18nLoader, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
9282
9560
  //# sourceMappingURL=decaf-ts-for-angular.mjs.map