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