platformcommons-web-lib 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commons-shared-web-ui-1.0.0.tgz +0 -0
- package/documentation/alert.md +123 -0
- package/documentation/button-dropdown.md +126 -0
- package/documentation/button.md +184 -0
- package/documentation/cards-usage-guidelines.md +131 -0
- package/documentation/configurable-form.md +605 -0
- package/documentation/confirmation-modal.md +250 -0
- package/documentation/filter-sidebar.md +178 -0
- package/documentation/filter-table-selector.md +228 -0
- package/documentation/form-builder.md +597 -0
- package/documentation/form-components.md +384 -0
- package/documentation/nav.md +427 -0
- package/documentation/pagination.md +181 -0
- package/documentation/side-nav-documentation.md +169 -0
- package/documentation/smart-form.md +2177 -0
- package/documentation/smart-table.md +1198 -0
- package/documentation/snackbar.md +118 -0
- package/documentation/style-externalization.md +88 -0
- package/documentation/summary-card.md +279 -0
- package/ng-package.json +28 -0
- package/package.json +54 -0
- package/src/lib/modules/alert/alert.models.ts +6 -0
- package/src/lib/modules/alert/alert.module.ts +16 -0
- package/src/lib/modules/alert/alert.theme.scss +85 -0
- package/src/lib/modules/alert/components/alert/alert.component.html +27 -0
- package/src/lib/modules/alert/components/alert/alert.component.scss +92 -0
- package/src/lib/modules/alert/components/alert/alert.component.ts +81 -0
- package/src/lib/modules/button/button.models.ts +13 -0
- package/src/lib/modules/button/button.module.ts +16 -0
- package/src/lib/modules/button/button.theme.scss +121 -0
- package/src/lib/modules/button/components/button/button.component.html +22 -0
- package/src/lib/modules/button/components/button/button.component.scss +88 -0
- package/src/lib/modules/button/components/button/button.component.ts +67 -0
- package/src/lib/modules/button-dropdown/button-dropdown.models.ts +26 -0
- package/src/lib/modules/button-dropdown/button-dropdown.module.ts +22 -0
- package/src/lib/modules/button-dropdown/button-dropdown.theme.scss +87 -0
- package/src/lib/modules/button-dropdown/components/button-dropdown/button-dropdown.component.html +41 -0
- package/src/lib/modules/button-dropdown/components/button-dropdown/button-dropdown.component.scss +135 -0
- package/src/lib/modules/button-dropdown/components/button-dropdown/button-dropdown.component.ts +160 -0
- package/src/lib/modules/configurable-form/component/configurable-form.component.html +294 -0
- package/src/lib/modules/configurable-form/component/configurable-form.component.scss +503 -0
- package/src/lib/modules/configurable-form/component/configurable-form.component.ts +628 -0
- package/src/lib/modules/configurable-form/configurable-form.examples.ts +154 -0
- package/src/lib/modules/configurable-form/configurable-form.model.ts +131 -0
- package/src/lib/modules/configurable-form/configurable-form.module.ts +19 -0
- package/src/lib/modules/configurable-form/configurable-form.theme.scss +78 -0
- package/src/lib/modules/confirmation-modal/components/confirmation-modal/confirmation-modal.component.html +77 -0
- package/src/lib/modules/confirmation-modal/components/confirmation-modal/confirmation-modal.component.scss +395 -0
- package/src/lib/modules/confirmation-modal/components/confirmation-modal/confirmation-modal.component.ts +266 -0
- package/src/lib/modules/confirmation-modal/confirmation-modal.models.ts +71 -0
- package/src/lib/modules/confirmation-modal/confirmation-modal.module.ts +20 -0
- package/src/lib/modules/confirmation-modal/confirmation-modal.theme.scss +87 -0
- package/src/lib/modules/filter/components/filter/filter.component.html +131 -0
- package/src/lib/modules/filter/components/filter/filter.component.scss +245 -0
- package/src/lib/modules/filter/components/filter/filter.component.ts +216 -0
- package/src/lib/modules/filter/filter.models.ts +88 -0
- package/src/lib/modules/filter/filter.module.ts +24 -0
- package/src/lib/modules/filter/filter.theme.scss +92 -0
- package/src/lib/modules/filter-sidebar/components/filter-sidebar/filter-sidebar.component.html +112 -0
- package/src/lib/modules/filter-sidebar/components/filter-sidebar/filter-sidebar.component.scss +186 -0
- package/src/lib/modules/filter-sidebar/components/filter-sidebar/filter-sidebar.component.ts +163 -0
- package/src/lib/modules/filter-sidebar/filter-sidebar.models.ts +95 -0
- package/src/lib/modules/filter-sidebar/filter-sidebar.module.ts +24 -0
- package/src/lib/modules/filter-sidebar/filter-sidebar.theme.scss +38 -0
- package/src/lib/modules/filter-table-selector/components/filter-table-selector/filter-table-selector.component.html +73 -0
- package/src/lib/modules/filter-table-selector/components/filter-table-selector/filter-table-selector.component.scss +321 -0
- package/src/lib/modules/filter-table-selector/components/filter-table-selector/filter-table-selector.component.ts +361 -0
- package/src/lib/modules/filter-table-selector/filter-table-selector.models.ts +91 -0
- package/src/lib/modules/filter-table-selector/filter-table-selector.module.ts +22 -0
- package/src/lib/modules/filter-table-selector/filter-table-selector.theme.scss +36 -0
- package/src/lib/modules/form-builder/components/field-configurator/configurator-config-panel/configurator-config-panel.component.html +63 -0
- package/src/lib/modules/form-builder/components/field-configurator/configurator-config-panel/configurator-config-panel.component.scss +496 -0
- package/src/lib/modules/form-builder/components/field-configurator/configurator-config-panel/configurator-config-panel.component.ts +445 -0
- package/src/lib/modules/form-builder/components/field-configurator/configurator-tree/configurator-tree.component.html +75 -0
- package/src/lib/modules/form-builder/components/field-configurator/configurator-tree/configurator-tree.component.scss +210 -0
- package/src/lib/modules/form-builder/components/field-configurator/configurator-tree/configurator-tree.component.ts +55 -0
- package/src/lib/modules/form-builder/components/field-configurator/field-configurator.component.html +25 -0
- package/src/lib/modules/form-builder/components/field-configurator/field-configurator.component.scss +82 -0
- package/src/lib/modules/form-builder/components/field-configurator/field-configurator.component.ts +95 -0
- package/src/lib/modules/form-builder/components/field-selection/field-selection.component.html +20 -0
- package/src/lib/modules/form-builder/components/field-selection/field-selection.component.scss +37 -0
- package/src/lib/modules/form-builder/components/field-selection/field-selection.component.ts +94 -0
- package/src/lib/modules/form-builder/components/field-selection/group-node/group-node.component.html +46 -0
- package/src/lib/modules/form-builder/components/field-selection/group-node/group-node.component.scss +102 -0
- package/src/lib/modules/form-builder/components/field-selection/group-node/group-node.component.ts +50 -0
- package/src/lib/modules/form-builder/components/field-selection/selection-field-node/selection-field-node.component.html +35 -0
- package/src/lib/modules/form-builder/components/field-selection/selection-field-node/selection-field-node.component.scss +67 -0
- package/src/lib/modules/form-builder/components/field-selection/selection-field-node/selection-field-node.component.ts +34 -0
- package/src/lib/modules/form-builder/components/field-selection/selection-section-node/selection-section-node.component.html +68 -0
- package/src/lib/modules/form-builder/components/field-selection/selection-section-node/selection-section-node.component.scss +113 -0
- package/src/lib/modules/form-builder/components/field-selection/selection-section-node/selection-section-node.component.ts +74 -0
- package/src/lib/modules/form-builder/configs/field-type-schema.map.ts +533 -0
- package/src/lib/modules/form-builder/form-builder.module.ts +36 -0
- package/src/lib/modules/form-builder/form-builder.theme.scss +212 -0
- package/src/lib/modules/form-builder/index.ts +9 -0
- package/src/lib/modules/form-builder/models/builder.models.ts +7 -0
- package/src/lib/modules/form-builder/models/field-configurator.models.ts +38 -0
- package/src/lib/modules/form-builder/models/field-selection.models.ts +51 -0
- package/src/lib/modules/form-builder/services/field-configurator.service.ts +258 -0
- package/src/lib/modules/form-builder/services/field-selection.service.ts +300 -0
- package/src/lib/modules/form-builder/services/form-schema-tree.service.ts +652 -0
- package/src/lib/modules/form-builder/tokens/builder.tokens.ts +10 -0
- package/src/lib/modules/form-builder/utils/constants.ts +43 -0
- package/src/lib/modules/form-components/components/checkbox/_theme.scss +63 -0
- package/src/lib/modules/form-components/components/checkbox/checkbox.component.html +29 -0
- package/src/lib/modules/form-components/components/checkbox/checkbox.component.scss +111 -0
- package/src/lib/modules/form-components/components/checkbox/checkbox.component.ts +207 -0
- package/src/lib/modules/form-components/components/checkbox/checkbox.models.ts +35 -0
- package/src/lib/modules/form-components/components/datepicker/_theme.scss +82 -0
- package/src/lib/modules/form-components/components/datepicker/datepicker.component.html +42 -0
- package/src/lib/modules/form-components/components/datepicker/datepicker.component.scss +115 -0
- package/src/lib/modules/form-components/components/datepicker/datepicker.component.ts +267 -0
- package/src/lib/modules/form-components/components/datepicker/datepicker.models.ts +45 -0
- package/src/lib/modules/form-components/components/dropdown/_theme.scss +91 -0
- package/src/lib/modules/form-components/components/dropdown/dropdown.component.html +74 -0
- package/src/lib/modules/form-components/components/dropdown/dropdown.component.scss +252 -0
- package/src/lib/modules/form-components/components/dropdown/dropdown.component.ts +377 -0
- package/src/lib/modules/form-components/components/dropdown/dropdown.models.ts +53 -0
- package/src/lib/modules/form-components/components/input/_theme.scss +77 -0
- package/src/lib/modules/form-components/components/input/input.component.html +51 -0
- package/src/lib/modules/form-components/components/input/input.component.scss +128 -0
- package/src/lib/modules/form-components/components/input/input.component.ts +250 -0
- package/src/lib/modules/form-components/components/input/input.models.ts +55 -0
- package/src/lib/modules/form-components/components/radio/_theme.scss +61 -0
- package/src/lib/modules/form-components/components/radio/radio.component.html +22 -0
- package/src/lib/modules/form-components/components/radio/radio.component.scss +107 -0
- package/src/lib/modules/form-components/components/radio/radio.component.ts +181 -0
- package/src/lib/modules/form-components/components/radio/radio.models.ts +39 -0
- package/src/lib/modules/form-components/components/search/_theme.scss +73 -0
- package/src/lib/modules/form-components/components/search/search.component.html +15 -0
- package/src/lib/modules/form-components/components/search/search.component.scss +87 -0
- package/src/lib/modules/form-components/components/search/search.component.ts +213 -0
- package/src/lib/modules/form-components/components/search/search.models.ts +40 -0
- package/src/lib/modules/form-components/components/toggle/_theme.scss +45 -0
- package/src/lib/modules/form-components/components/toggle/toggle.component.html +15 -0
- package/src/lib/modules/form-components/components/toggle/toggle.component.scss +81 -0
- package/src/lib/modules/form-components/components/toggle/toggle.component.ts +166 -0
- package/src/lib/modules/form-components/components/toggle/toggle.models.ts +27 -0
- package/src/lib/modules/form-components/directives/click-outside.directive.ts +22 -0
- package/src/lib/modules/form-components/form-components.module.ts +41 -0
- package/src/lib/modules/form-components/form-components.theme.scss +25 -0
- package/src/lib/modules/material/material.module.ts +94 -0
- package/src/lib/modules/nav/components/nav/nav.component.html +34 -0
- package/src/lib/modules/nav/components/nav/nav.component.scss +171 -0
- package/src/lib/modules/nav/components/nav/nav.component.ts +82 -0
- package/src/lib/modules/nav/nav.models.ts +31 -0
- package/src/lib/modules/nav/nav.module.ts +17 -0
- package/src/lib/modules/nav/nav.theme.scss +86 -0
- package/src/lib/modules/pagination/components/pagination/pagination.component.html +52 -0
- package/src/lib/modules/pagination/components/pagination/pagination.component.scss +155 -0
- package/src/lib/modules/pagination/components/pagination/pagination.component.ts +109 -0
- package/src/lib/modules/pagination/pagination.module.ts +17 -0
- package/src/lib/modules/pagination/pagination.theme.scss +66 -0
- package/src/lib/modules/side-nav/components/side-nav/side-nav.component.html +56 -0
- package/src/lib/modules/side-nav/components/side-nav/side-nav.component.scss +342 -0
- package/src/lib/modules/side-nav/components/side-nav/side-nav.component.ts +135 -0
- package/src/lib/modules/side-nav/side-nav.models.ts +38 -0
- package/src/lib/modules/side-nav/side-nav.module.ts +16 -0
- package/src/lib/modules/side-nav/side-nav.theme.scss +111 -0
- package/src/lib/modules/smart-form/components/form-field/form-field.component.html +1109 -0
- package/src/lib/modules/smart-form/components/form-field/form-field.component.scss +1860 -0
- package/src/lib/modules/smart-form/components/form-field/form-field.component.ts +2232 -0
- package/src/lib/modules/smart-form/components/form-section/form-section.component.html +64 -0
- package/src/lib/modules/smart-form/components/form-section/form-section.component.scss +209 -0
- package/src/lib/modules/smart-form/components/form-section/form-section.component.ts +119 -0
- package/src/lib/modules/smart-form/components/smart-form/smart-form.component.html +253 -0
- package/src/lib/modules/smart-form/components/smart-form/smart-form.component.scss +689 -0
- package/src/lib/modules/smart-form/components/smart-form/smart-form.component.ts +1087 -0
- package/src/lib/modules/smart-form/index.ts +10 -0
- package/src/lib/modules/smart-form/models/form-schema.model.ts +700 -0
- package/src/lib/modules/smart-form/models/hierarchy-config.model.ts +21 -0
- package/src/lib/modules/smart-form/services/expression.service.ts +75 -0
- package/src/lib/modules/smart-form/services/smart-form-controller.service.ts +65 -0
- package/src/lib/modules/smart-form/smart-form.examples.ts +1324 -0
- package/src/lib/modules/smart-form/smart-form.module.ts +36 -0
- package/src/lib/modules/smart-form/smart-form.theme.scss +890 -0
- package/src/lib/modules/smart-form/utils/translation.utils.ts +82 -0
- package/src/lib/modules/smart-form/utils/trusted-url.pipe.ts +25 -0
- package/src/lib/modules/smart-form/utils/validation.utils.ts +98 -0
- package/src/lib/modules/smart-table/components/smart-table/smart-table.component.html +283 -0
- package/src/lib/modules/smart-table/components/smart-table/smart-table.component.scss +685 -0
- package/src/lib/modules/smart-table/components/smart-table/smart-table.component.ts +1118 -0
- package/src/lib/modules/smart-table/models/table-config.model.ts +202 -0
- package/src/lib/modules/smart-table/smart-table.module.ts +30 -0
- package/src/lib/modules/smart-table/smart-table.theme.scss +335 -0
- package/src/lib/modules/smart-table/utils/safe-html.pipe.ts +22 -0
- package/src/lib/modules/smart-table/utils/smart-table.utils.ts +18 -0
- package/src/lib/modules/snackbar/components/snackbar.component.html +41 -0
- package/src/lib/modules/snackbar/components/snackbar.component.scss +99 -0
- package/src/lib/modules/snackbar/components/snackbar.component.ts +18 -0
- package/src/lib/modules/snackbar/models/snackbar.models.ts +10 -0
- package/src/lib/modules/snackbar/services/snackbar.service.ts +40 -0
- package/src/lib/modules/snackbar/snackbar.module.ts +11 -0
- package/src/lib/modules/snackbar/snackbar.theme.scss +93 -0
- package/src/lib/modules/summary-card/components/summary-card/summary-card.component.html +47 -0
- package/src/lib/modules/summary-card/components/summary-card/summary-card.component.scss +199 -0
- package/src/lib/modules/summary-card/components/summary-card/summary-card.component.ts +126 -0
- package/src/lib/modules/summary-card/summary-card.module.ts +18 -0
- package/src/lib/modules/summary-card/summary-card.theme.scss +176 -0
- package/src/lib/shared-ui.module.ts +44 -0
- package/src/lib/styles/global.scss +152 -0
- package/src/lib/styles/utilities.scss +250 -0
- package/src/lib/utils/constants.ts +11 -0
- package/src/lib/utils/storage.utils.ts +37 -0
- package/src/lib/utils/string.utils.ts +23 -0
- package/src/lib/utils/translation.utils.ts +87 -0
- package/src/public-api.ts +104 -0
- package/tsconfig.lib.json +15 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
FormSchema,
|
|
4
|
+
FieldConfig,
|
|
5
|
+
SectionConfig,
|
|
6
|
+
} from '../../smart-form/models/form-schema.model';
|
|
7
|
+
import {
|
|
8
|
+
SelectionGroupNode,
|
|
9
|
+
SelectionSectionNode,
|
|
10
|
+
SelectionFieldNode,
|
|
11
|
+
} from '../models/field-selection.models';
|
|
12
|
+
import {
|
|
13
|
+
ConfiguratorTreeNode,
|
|
14
|
+
} from '../models/field-configurator.models';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Service to parse a FormSchema into selection/configurator tree structures,
|
|
18
|
+
* and to rebuild a modified FormSchema from those trees.
|
|
19
|
+
*/
|
|
20
|
+
@Injectable({ providedIn: 'root' })
|
|
21
|
+
export class FormSchemaTreeService {
|
|
22
|
+
|
|
23
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
// SELECTION TREE
|
|
25
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse a FormSchema into SelectionGroupNodes for the field-selection UI.
|
|
29
|
+
* Stepper forms produce one group per step; section forms produce one group.
|
|
30
|
+
*/
|
|
31
|
+
parseSchemaToSelectionGroups(schema: FormSchema): SelectionGroupNode[] {
|
|
32
|
+
if (schema.formType === 'STEPPER' && schema.stepperConfig?.children?.length) {
|
|
33
|
+
return schema.stepperConfig.children
|
|
34
|
+
.filter(child => child.type === 'GROUP')
|
|
35
|
+
.map(groupField => this._buildGroupNode(groupField));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// SECTION form → single virtual group
|
|
39
|
+
if (schema.sectionConfig?.children?.length) {
|
|
40
|
+
return [{
|
|
41
|
+
label: schema.label || 'Form',
|
|
42
|
+
groupFieldConfig: null,
|
|
43
|
+
enabled: true,
|
|
44
|
+
expanded: true, // Collapse by default for better UX
|
|
45
|
+
sections: this._buildSectionsFromChildren(schema.sectionConfig.children),
|
|
46
|
+
}];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Rebuild a FormSchema from the selection tree, updating visible/disabled/required
|
|
54
|
+
* flags on each FieldConfig. Does NOT mutate the original schema.
|
|
55
|
+
*/
|
|
56
|
+
applySelectionToSchema(
|
|
57
|
+
groups: SelectionGroupNode[],
|
|
58
|
+
originalSchema: FormSchema,
|
|
59
|
+
): FormSchema {
|
|
60
|
+
const schema: FormSchema = JSON.parse(JSON.stringify(originalSchema));
|
|
61
|
+
|
|
62
|
+
if (schema.formType === 'STEPPER' && schema.stepperConfig?.children?.length) {
|
|
63
|
+
const stepChildren = schema.stepperConfig.children.filter(c => c.type === 'GROUP');
|
|
64
|
+
groups.forEach((group, gi) => {
|
|
65
|
+
if (gi < stepChildren.length) {
|
|
66
|
+
const groupField = stepChildren[gi];
|
|
67
|
+
groupField.isEnabled = group.enabled;
|
|
68
|
+
groupField.visible = group.enabled; // Backward compatibility
|
|
69
|
+
groupField.disabled = !group.enabled;
|
|
70
|
+
if (groupField.sectionConfig?.children) {
|
|
71
|
+
this._applySectionsToChildren(group.sections, groupField.sectionConfig.children);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
} else if (schema.sectionConfig?.children?.length && groups.length > 0) {
|
|
76
|
+
this._applySectionsToChildren(groups[0].sections, schema.sectionConfig.children);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return schema;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
83
|
+
// CONFIGURATOR TREE
|
|
84
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Parse a FormSchema into a flat ConfiguratorTreeNode[] for the left-panel tree.
|
|
88
|
+
*/
|
|
89
|
+
parseSchemaToConfiguratorTree(schema: FormSchema): ConfiguratorTreeNode[] {
|
|
90
|
+
if (schema.formType === 'STEPPER' && schema.stepperConfig?.children?.length) {
|
|
91
|
+
return schema.stepperConfig.children
|
|
92
|
+
.filter(c => c.type === 'GROUP')
|
|
93
|
+
.map((groupField, gi) => this._buildConfiguratorGroupNode(groupField, [gi]));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (schema.sectionConfig?.children?.length) {
|
|
97
|
+
return this._buildConfiguratorChildNodes(schema.sectionConfig.children, []);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Find a FieldConfig inside a FormSchema by its index path.
|
|
105
|
+
*/
|
|
106
|
+
findFieldByPath(schema: FormSchema, path: number[]): FieldConfig | null {
|
|
107
|
+
if (path.length === 0) return null;
|
|
108
|
+
|
|
109
|
+
let children: FieldConfig[] | undefined;
|
|
110
|
+
if (schema.formType === 'STEPPER') {
|
|
111
|
+
children = schema.stepperConfig?.children;
|
|
112
|
+
} else {
|
|
113
|
+
children = schema.sectionConfig?.children;
|
|
114
|
+
}
|
|
115
|
+
if (!children) return null;
|
|
116
|
+
|
|
117
|
+
let current: FieldConfig | undefined;
|
|
118
|
+
for (let i = 0; i < path.length; i++) {
|
|
119
|
+
const idx = path[i];
|
|
120
|
+
if (!children || idx >= children.length) return null;
|
|
121
|
+
current = children[idx];
|
|
122
|
+
|
|
123
|
+
if (i < path.length - 1) {
|
|
124
|
+
// Navigate deeper
|
|
125
|
+
if (current.sectionConfig?.children) {
|
|
126
|
+
children = current.sectionConfig.children;
|
|
127
|
+
} else if (current.children) {
|
|
128
|
+
children = current.children;
|
|
129
|
+
} else {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return current ?? null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Apply a partial patch to a FieldConfig located at the given path.
|
|
140
|
+
* Returns a new FormSchema (does not mutate the original).
|
|
141
|
+
*/
|
|
142
|
+
/**
|
|
143
|
+
* Known top-level keys of FieldConfig.
|
|
144
|
+
* Used to strip stale group-name keys (e.g. textConstraintsTextConfig) that
|
|
145
|
+
* may have been introduced by previous incorrect patch operations.
|
|
146
|
+
*/
|
|
147
|
+
private readonly _VALID_FIELD_KEYS = new Set([
|
|
148
|
+
'name', 'type', 'subType', 'label', 'placeholder', 'hint',
|
|
149
|
+
'required', 'disabled', 'readonly', 'isEnabled', 'visible',
|
|
150
|
+
'colSpan', 'defaultValue', 'prefix', 'suffix', 'className',
|
|
151
|
+
'payloadPath', 'errorMessage', 'visibilityExpression', 'onValidate',
|
|
152
|
+
'children', 'sectionConfig', 'rows',
|
|
153
|
+
'textConfig', 'emailConfig', 'phoneConfig', 'numberConfig',
|
|
154
|
+
'dateConfig', 'optionConfig', 'attachmentConfig', 'richTextConfig',
|
|
155
|
+
'ratingConfig', 'locationConfig', 'generatedConfig', 'rangeConfig',
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
applyFieldPatchToSchema(
|
|
159
|
+
path: number[],
|
|
160
|
+
patch: Partial<FieldConfig>,
|
|
161
|
+
originalSchema: FormSchema,
|
|
162
|
+
): FormSchema {
|
|
163
|
+
const schema: FormSchema = JSON.parse(JSON.stringify(originalSchema));
|
|
164
|
+
const field = this.findFieldByPath(schema, path) as Record<string, any> | null;
|
|
165
|
+
if (field) {
|
|
166
|
+
// 1. Strip stale keys that don't belong to FieldConfig (e.g. old group names)
|
|
167
|
+
for (const key of Object.keys(field)) {
|
|
168
|
+
if (!this._VALID_FIELD_KEYS.has(key)) {
|
|
169
|
+
delete field[key];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// 2. Deep-merge the new patch on top of the cleaned field
|
|
173
|
+
FormSchemaTreeService.deepMerge(field, patch as any);
|
|
174
|
+
// 3. Clean up empty-string / null leaves from nested config objects.
|
|
175
|
+
// e.g. user clears minDate → dateConfig.minDate becomes "" → remove it.
|
|
176
|
+
// If the entire config object ends up empty → remove the key.
|
|
177
|
+
FormSchemaTreeService.cleanEmptyConfigValues(field);
|
|
178
|
+
}
|
|
179
|
+
return schema;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Remove empty-string and null values from all typed config sub-objects on a field.
|
|
184
|
+
* If all values in a config sub-object are empty, the key is deleted entirely.
|
|
185
|
+
*/
|
|
186
|
+
public static cleanEmptyConfigValues(field: Record<string, any>): void {
|
|
187
|
+
const CONFIG_KEYS = [
|
|
188
|
+
'textConfig', 'emailConfig', 'phoneConfig', 'numberConfig',
|
|
189
|
+
'dateConfig', 'optionConfig', 'attachmentConfig', 'richTextConfig',
|
|
190
|
+
'ratingConfig', 'locationConfig', 'generatedConfig', 'rangeConfig',
|
|
191
|
+
];
|
|
192
|
+
for (const key of CONFIG_KEYS) {
|
|
193
|
+
if (field[key] && typeof field[key] === 'object' && !Array.isArray(field[key])) {
|
|
194
|
+
field[key] = FormSchemaTreeService._removeEmptyLeaves(field[key]);
|
|
195
|
+
if (Object.keys(field[key]).length === 0) {
|
|
196
|
+
delete field[key];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Recursively strip empty-string, null, and undefined values from a plain object.
|
|
204
|
+
* Nested objects that become empty after stripping are also removed.
|
|
205
|
+
*/
|
|
206
|
+
private static _removeEmptyLeaves(obj: Record<string, any>): Record<string, any> {
|
|
207
|
+
const result: Record<string, any> = {};
|
|
208
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
209
|
+
if (val === '' || val === null || val === undefined) continue;
|
|
210
|
+
if (typeof val === 'object' && !Array.isArray(val)) {
|
|
211
|
+
const nested = FormSchemaTreeService._removeEmptyLeaves(val);
|
|
212
|
+
if (Object.keys(nested).length > 0) result[key] = nested;
|
|
213
|
+
} else {
|
|
214
|
+
result[key] = val;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Recursively merge `source` into `target` in-place.
|
|
222
|
+
* Objects are merged key-by-key; primitives/arrays replace the existing value.
|
|
223
|
+
*/
|
|
224
|
+
public static deepMerge(target: Record<string, any>, source: Record<string, any>): void {
|
|
225
|
+
for (const key of Object.keys(source)) {
|
|
226
|
+
const srcVal = source[key];
|
|
227
|
+
const tgtVal = target[key];
|
|
228
|
+
if (
|
|
229
|
+
srcVal !== null &&
|
|
230
|
+
typeof srcVal === 'object' &&
|
|
231
|
+
!Array.isArray(srcVal) &&
|
|
232
|
+
tgtVal !== null &&
|
|
233
|
+
typeof tgtVal === 'object' &&
|
|
234
|
+
!Array.isArray(tgtVal)
|
|
235
|
+
) {
|
|
236
|
+
// Both sides are plain objects — merge recursively
|
|
237
|
+
this.deepMerge(tgtVal, srcVal);
|
|
238
|
+
} else {
|
|
239
|
+
target[key] = srcVal;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Convert a flat object with dot-notation keys into a nested object.
|
|
246
|
+
* E.g. { 'a.b.c': 1 } -> { a: { b: { c: 1 } } }
|
|
247
|
+
*/
|
|
248
|
+
public static inflatePatch(flatPatch: Record<string, any>): Record<string, any> {
|
|
249
|
+
const nested: Record<string, any> = {};
|
|
250
|
+
|
|
251
|
+
for (const [key, value] of Object.entries(flatPatch)) {
|
|
252
|
+
const parts = key.split('.');
|
|
253
|
+
let current = nested;
|
|
254
|
+
|
|
255
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
256
|
+
const part = parts[i];
|
|
257
|
+
if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {
|
|
258
|
+
current[part] = {};
|
|
259
|
+
}
|
|
260
|
+
current = current[part];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
current[parts[parts.length - 1]] = value;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return nested;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Convert a nested object into a flat object with dot-notation keys.
|
|
271
|
+
* E.g. { a: { b: { c: 1 } } } -> { 'a.b.c': 1 }
|
|
272
|
+
*/
|
|
273
|
+
public static flattenObject(obj: Record<string, any>, prefix = ''): Record<string, any> {
|
|
274
|
+
const flat: Record<string, any> = {};
|
|
275
|
+
|
|
276
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
277
|
+
const newKey = prefix ? `${prefix}.${key}` : key;
|
|
278
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
279
|
+
Object.assign(flat, this.flattenObject(value, newKey));
|
|
280
|
+
} else {
|
|
281
|
+
flat[newKey] = value;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return flat;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
289
|
+
// FIELD TYPE MAPPING (FormSchema type → builder field type)
|
|
290
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Map a FormSchema FieldConfig.type to the builder fieldType key
|
|
294
|
+
* used in field-type-schema.map.ts for config UI generation.
|
|
295
|
+
*/
|
|
296
|
+
mapFieldTypeToBuilderType(type: string, subType?: string): string {
|
|
297
|
+
const typeMap: Record<string, string> = {
|
|
298
|
+
'TEXT_INPUT': 'shortText',
|
|
299
|
+
'TEXT': 'shortText',
|
|
300
|
+
'TEXTAREA': 'longText',
|
|
301
|
+
'RICH_TEXT': 'richText',
|
|
302
|
+
'NUMBER_INPUT': 'number',
|
|
303
|
+
'NUMBER': 'number',
|
|
304
|
+
'DATE': 'date',
|
|
305
|
+
'TIME': 'time',
|
|
306
|
+
'DROPDOWN': 'dropdown',
|
|
307
|
+
'SELECT': 'dropdown',
|
|
308
|
+
'MULTI_SELECT': 'multiSelect',
|
|
309
|
+
'AUTOCOMPLETE': 'autocomplete',
|
|
310
|
+
'RADIO': 'radio',
|
|
311
|
+
'CHECKBOX': 'checkbox',
|
|
312
|
+
'SWITCH': 'toggle',
|
|
313
|
+
'FILE_UPLOAD': 'file',
|
|
314
|
+
'RATING': 'rating',
|
|
315
|
+
'GENERATED': 'generated',
|
|
316
|
+
'CHIP': 'chip',
|
|
317
|
+
'LOCATION': 'location',
|
|
318
|
+
'GROUP': 'group',
|
|
319
|
+
'SUBFIELDS': 'section',
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// Refinement by subType
|
|
323
|
+
if (type === 'TEXT_INPUT' && subType) {
|
|
324
|
+
switch (subType) {
|
|
325
|
+
case 'LONG': return 'longText';
|
|
326
|
+
case 'EMAIL': return 'email';
|
|
327
|
+
case 'PHONE': return 'phone';
|
|
328
|
+
case 'PASSWORD': return 'password';
|
|
329
|
+
default: return 'shortText';
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (type === 'NUMBER_INPUT') {
|
|
334
|
+
if (subType === 'DECIMAL') return 'decimal';
|
|
335
|
+
return 'number';
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (type === 'DATE') {
|
|
339
|
+
if (subType === 'DATETIME') return 'dateTime';
|
|
340
|
+
return 'date';
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (type === 'CHECKBOX' && subType === 'LIST') {
|
|
344
|
+
return 'multiCheckbox';
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return typeMap[type] ?? 'shortText';
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
351
|
+
// PRIVATE — Selection tree builders
|
|
352
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
353
|
+
|
|
354
|
+
private _buildGroupNode(groupField: FieldConfig): SelectionGroupNode {
|
|
355
|
+
const sections: SelectionSectionNode[] = [];
|
|
356
|
+
|
|
357
|
+
if (groupField.sectionConfig?.children) {
|
|
358
|
+
const built = this._buildSectionsFromChildren(groupField.sectionConfig.children);
|
|
359
|
+
sections.push(...built);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
label: groupField.sectionConfig?.label || groupField.label || groupField.name || 'Group',
|
|
364
|
+
groupFieldConfig: groupField,
|
|
365
|
+
enabled: groupField.isEnabled ?? groupField.visible ?? true,
|
|
366
|
+
expanded: true, // Collapse by default for better UX - user can expand as needed
|
|
367
|
+
sections,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Build sections from a children array. Fields that are GROUP/SUBFIELDS
|
|
373
|
+
* become sections; other leaf fields are collected into a "default" section.
|
|
374
|
+
*/
|
|
375
|
+
private _buildSectionsFromChildren(children: FieldConfig[]): SelectionSectionNode[] {
|
|
376
|
+
const sections: SelectionSectionNode[] = [];
|
|
377
|
+
let leafFields: SelectionFieldNode[] = [];
|
|
378
|
+
|
|
379
|
+
for (const child of children) {
|
|
380
|
+
if (child.type === 'GROUP' || (child.type === 'SUBFIELDS') || child.sectionConfig) {
|
|
381
|
+
// Flush any accumulated leaf fields into a default section
|
|
382
|
+
if (leafFields.length > 0) {
|
|
383
|
+
sections.push(this._createDefaultSection(leafFields));
|
|
384
|
+
leafFields = [];
|
|
385
|
+
}
|
|
386
|
+
sections.push(this._buildSectionNode(child));
|
|
387
|
+
} else if (child.type === 'ROW' && child.children?.length) {
|
|
388
|
+
// ROW children are leaf fields
|
|
389
|
+
for (const rowChild of child.children) {
|
|
390
|
+
leafFields.push(this._buildFieldNode(rowChild));
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
leafFields.push(this._buildFieldNode(child));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Flush remaining leaf fields
|
|
398
|
+
if (leafFields.length > 0) {
|
|
399
|
+
sections.push(this._createDefaultSection(leafFields));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return sections;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private _buildSectionNode(field: FieldConfig): SelectionSectionNode {
|
|
406
|
+
const subsections: SelectionSectionNode[] = [];
|
|
407
|
+
const fields: SelectionFieldNode[] = [];
|
|
408
|
+
|
|
409
|
+
const children = field.sectionConfig?.children ?? field.children ?? [];
|
|
410
|
+
for (const child of children) {
|
|
411
|
+
if (child.type === 'GROUP' || child.sectionConfig) {
|
|
412
|
+
subsections.push(this._buildSectionNode(child));
|
|
413
|
+
} else if (child.type === 'ROW' && child.children?.length) {
|
|
414
|
+
for (const rowChild of child.children) {
|
|
415
|
+
fields.push(this._buildFieldNode(rowChild));
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
fields.push(this._buildFieldNode(child));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const isVisible = field.isEnabled ?? field.visible ?? true;
|
|
423
|
+
return {
|
|
424
|
+
label: field.sectionConfig?.label || field.label || field.name || 'Section',
|
|
425
|
+
sectionConfig: field.sectionConfig ?? null,
|
|
426
|
+
parentFieldConfig: field,
|
|
427
|
+
enabled: isVisible && field.disabled !== true,
|
|
428
|
+
expanded: false, // Collapse by default for better UX - user can expand as needed
|
|
429
|
+
fields,
|
|
430
|
+
subsections,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
private _createDefaultSection(fields: SelectionFieldNode[]): SelectionSectionNode {
|
|
435
|
+
return {
|
|
436
|
+
label: 'Fields',
|
|
437
|
+
sectionConfig: null,
|
|
438
|
+
parentFieldConfig: null,
|
|
439
|
+
enabled: true,
|
|
440
|
+
expanded: false, // Collapse by default for better UX - user can expand as needed
|
|
441
|
+
fields,
|
|
442
|
+
subsections: [],
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private _buildFieldNode(field: FieldConfig): SelectionFieldNode {
|
|
447
|
+
return {
|
|
448
|
+
fieldConfig: field,
|
|
449
|
+
selected: field.isEnabled ?? field.visible ?? true,
|
|
450
|
+
isLocked: field.readonly === true,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
455
|
+
// PRIVATE — Apply selection back to FormSchema children
|
|
456
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
457
|
+
|
|
458
|
+
private _applySectionsToChildren(
|
|
459
|
+
sections: SelectionSectionNode[],
|
|
460
|
+
children: FieldConfig[],
|
|
461
|
+
): void {
|
|
462
|
+
let sectionIdx = 0;
|
|
463
|
+
let fieldIdx = 0;
|
|
464
|
+
|
|
465
|
+
for (const child of children) {
|
|
466
|
+
if (child.type === 'GROUP' || child.sectionConfig) {
|
|
467
|
+
if (sectionIdx < sections.length) {
|
|
468
|
+
const section = sections[sectionIdx];
|
|
469
|
+
child.isEnabled = section.enabled;
|
|
470
|
+
child.visible = section.enabled; // Backward compatibility
|
|
471
|
+
child.disabled = !section.enabled;
|
|
472
|
+
const innerChildren = child.sectionConfig?.children ?? child.children ?? [];
|
|
473
|
+
if (innerChildren.length > 0) {
|
|
474
|
+
this._applySectionsToChildren(section.subsections, innerChildren);
|
|
475
|
+
// Also apply field selections
|
|
476
|
+
this._applyFieldsToChildren(section.fields, innerChildren);
|
|
477
|
+
}
|
|
478
|
+
sectionIdx++;
|
|
479
|
+
}
|
|
480
|
+
} else if (child.type === 'ROW' && child.children?.length) {
|
|
481
|
+
for (const rowChild of child.children) {
|
|
482
|
+
const matchedField = this._findSelectionField(sections, rowChild, fieldIdx);
|
|
483
|
+
if (matchedField) {
|
|
484
|
+
rowChild.isEnabled = matchedField.selected;
|
|
485
|
+
rowChild.visible = matchedField.selected; // Backward compatibility
|
|
486
|
+
fieldIdx++;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
const matchedField = this._findSelectionField(sections, child, fieldIdx);
|
|
491
|
+
if (matchedField) {
|
|
492
|
+
child.isEnabled = matchedField.selected;
|
|
493
|
+
child.visible = matchedField.selected; // Backward compatibility
|
|
494
|
+
fieldIdx++;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
private _applyFieldsToChildren(
|
|
501
|
+
selectionFields: SelectionFieldNode[],
|
|
502
|
+
children: FieldConfig[],
|
|
503
|
+
): void {
|
|
504
|
+
let fi = 0;
|
|
505
|
+
for (const child of children) {
|
|
506
|
+
if (child.type === 'GROUP' || child.sectionConfig) continue;
|
|
507
|
+
if (child.type === 'ROW' && child.children?.length) {
|
|
508
|
+
for (const rowChild of child.children) {
|
|
509
|
+
if (fi < selectionFields.length) {
|
|
510
|
+
rowChild.isEnabled = selectionFields[fi].selected;
|
|
511
|
+
rowChild.visible = selectionFields[fi].selected; // Backward compatibility
|
|
512
|
+
fi++;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
if (fi < selectionFields.length) {
|
|
517
|
+
child.isEnabled = selectionFields[fi].selected;
|
|
518
|
+
child.visible = selectionFields[fi].selected; // Backward compatibility
|
|
519
|
+
fi++;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
private _findSelectionField(
|
|
526
|
+
sections: SelectionSectionNode[],
|
|
527
|
+
_field: FieldConfig,
|
|
528
|
+
_index: number,
|
|
529
|
+
): SelectionFieldNode | null {
|
|
530
|
+
// Flatten all fields from all sections
|
|
531
|
+
for (const section of sections) {
|
|
532
|
+
for (const f of section.fields) {
|
|
533
|
+
if (f.fieldConfig === _field || f.fieldConfig.name === _field.name) {
|
|
534
|
+
return f;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
542
|
+
// PRIVATE — Configurator tree builders
|
|
543
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
544
|
+
|
|
545
|
+
private _buildConfiguratorGroupNode(
|
|
546
|
+
groupField: FieldConfig,
|
|
547
|
+
basePath: number[],
|
|
548
|
+
): ConfiguratorTreeNode {
|
|
549
|
+
const children: ConfiguratorTreeNode[] = [];
|
|
550
|
+
const sectionChildren = groupField.sectionConfig?.children ?? [];
|
|
551
|
+
|
|
552
|
+
sectionChildren.forEach((child, ci) => {
|
|
553
|
+
if (child.isEnabled === false || child.visible === false) return;
|
|
554
|
+
const childPath = [...basePath, ci];
|
|
555
|
+
if (child.type === 'GROUP' || child.type === 'SUBFIELDS' || child.subType === 'SECTION' || child.sectionConfig) {
|
|
556
|
+
children.push(this._buildConfiguratorSectionNode(child, childPath));
|
|
557
|
+
} else if (child.type === 'ROW' && child.children?.length) {
|
|
558
|
+
child.children.forEach((rowChild, ri) => {
|
|
559
|
+
if (rowChild.isEnabled !== false && rowChild.visible !== false) {
|
|
560
|
+
children.push(this._buildConfiguratorFieldNode(rowChild, [...childPath, ri]));
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
} else {
|
|
564
|
+
children.push(this._buildConfiguratorFieldNode(child, childPath));
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
return {
|
|
569
|
+
id: groupField.name || `group-${basePath.join('-')}`,
|
|
570
|
+
label: groupField.sectionConfig?.label || groupField.label || 'Group',
|
|
571
|
+
type: 'group',
|
|
572
|
+
expanded: false,
|
|
573
|
+
fieldConfig: groupField,
|
|
574
|
+
path: basePath,
|
|
575
|
+
children,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
private _buildConfiguratorSectionNode(
|
|
580
|
+
field: FieldConfig,
|
|
581
|
+
basePath: number[],
|
|
582
|
+
): ConfiguratorTreeNode {
|
|
583
|
+
const children: ConfiguratorTreeNode[] = [];
|
|
584
|
+
const sectionChildren = field.sectionConfig?.children ?? field.children ?? [];
|
|
585
|
+
|
|
586
|
+
sectionChildren.forEach((child, ci) => {
|
|
587
|
+
if (child.isEnabled === false || child.visible === false) return;
|
|
588
|
+
const childPath = [...basePath, ci];
|
|
589
|
+
if (child.type === 'GROUP' || child.type === 'SUBFIELDS' || child.subType === 'SECTION' || child.sectionConfig) {
|
|
590
|
+
children.push(this._buildConfiguratorSectionNode(child, childPath));
|
|
591
|
+
} else if (child.type === 'ROW' && child.children?.length) {
|
|
592
|
+
child.children.forEach((rowChild, ri) => {
|
|
593
|
+
if (rowChild.isEnabled !== false && rowChild.visible !== false) {
|
|
594
|
+
children.push(this._buildConfiguratorFieldNode(rowChild, [...childPath, ri]));
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
} else {
|
|
598
|
+
children.push(this._buildConfiguratorFieldNode(child, childPath));
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
return {
|
|
603
|
+
id: field.name || `section-${basePath.join('-')}`,
|
|
604
|
+
label: field.sectionConfig?.label || field.label || 'Section',
|
|
605
|
+
type: 'section',
|
|
606
|
+
expanded: false,
|
|
607
|
+
fieldConfig: field,
|
|
608
|
+
path: basePath,
|
|
609
|
+
children,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private _buildConfiguratorFieldNode(
|
|
614
|
+
field: FieldConfig,
|
|
615
|
+
path: number[],
|
|
616
|
+
): ConfiguratorTreeNode {
|
|
617
|
+
return {
|
|
618
|
+
id: field.name || `field-${path.join('-')}`,
|
|
619
|
+
label: field.label || field.name || 'Field',
|
|
620
|
+
type: 'field',
|
|
621
|
+
expanded: false,
|
|
622
|
+
fieldConfig: field,
|
|
623
|
+
path,
|
|
624
|
+
children: [],
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
private _buildConfiguratorChildNodes(
|
|
629
|
+
children: FieldConfig[],
|
|
630
|
+
basePath: number[],
|
|
631
|
+
): ConfiguratorTreeNode[] {
|
|
632
|
+
const nodes: ConfiguratorTreeNode[] = [];
|
|
633
|
+
|
|
634
|
+
children.forEach((child, ci) => {
|
|
635
|
+
if (child.isEnabled === false || child.visible === false) return;
|
|
636
|
+
const childPath = [...basePath, ci];
|
|
637
|
+
if (child.type === 'GROUP' || child.sectionConfig) {
|
|
638
|
+
nodes.push(this._buildConfiguratorSectionNode(child, childPath));
|
|
639
|
+
} else if (child.type === 'ROW' && child.children?.length) {
|
|
640
|
+
child.children.forEach((rowChild, ri) => {
|
|
641
|
+
if (rowChild.isEnabled !== false && rowChild.visible !== false) {
|
|
642
|
+
nodes.push(this._buildConfiguratorFieldNode(rowChild, [...childPath, ri]));
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
} else {
|
|
646
|
+
nodes.push(this._buildConfiguratorFieldNode(child, childPath));
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
return nodes;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { FieldTypeSchemaMap } from '../models/builder.models';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Optional token to override the field-type → config-schema mapping
|
|
6
|
+
* used by the standalone FieldConfiguratorComponent.
|
|
7
|
+
*/
|
|
8
|
+
export const BUILDER_FIELD_TYPE_SCHEMA_MAP = new InjectionToken<FieldTypeSchemaMap>(
|
|
9
|
+
'BUILDER_FIELD_TYPE_SCHEMA_MAP'
|
|
10
|
+
);
|