commons-shared-web-ui 0.0.62 → 0.0.64
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/fesm2022/commons-shared-web-ui.mjs +16368 -0
- package/fesm2022/commons-shared-web-ui.mjs.map +1 -0
- package/index.d.ts +5077 -0
- package/package.json +34 -53
- package/.claude/settings.local.json +0 -10
- package/documentation/alert.md +0 -123
- package/documentation/button-dropdown.md +0 -126
- package/documentation/button.md +0 -184
- package/documentation/cards-usage-guidelines.md +0 -131
- package/documentation/configurable-form.md +0 -605
- package/documentation/confirmation-modal.md +0 -250
- package/documentation/filter-sidebar.md +0 -178
- package/documentation/filter-table-selector.md +0 -228
- package/documentation/form-builder.md +0 -599
- package/documentation/form-components.md +0 -384
- package/documentation/form-field-configuration.md +0 -250
- package/documentation/nav.md +0 -427
- package/documentation/pagination.md +0 -181
- package/documentation/side-nav-documentation.md +0 -169
- package/documentation/smart-form.md +0 -2819
- package/documentation/smart-table.md +0 -1297
- package/documentation/snackbar.md +0 -118
- package/documentation/style-externalization.md +0 -88
- package/documentation/summary-card.md +0 -279
- package/documentation/table-column-configuration.md +0 -221
- package/documentation/time-picker.md +0 -274
- package/ng-package.json +0 -30
- package/src/lib/modules/alert/alert.models.ts +0 -6
- package/src/lib/modules/alert/alert.module.ts +0 -16
- package/src/lib/modules/alert/components/alert/alert.component.html +0 -27
- package/src/lib/modules/alert/components/alert/alert.component.scss +0 -92
- package/src/lib/modules/alert/components/alert/alert.component.ts +0 -81
- package/src/lib/modules/button/button.models.ts +0 -13
- package/src/lib/modules/button/button.module.ts +0 -16
- package/src/lib/modules/button/components/button/button.component.html +0 -22
- package/src/lib/modules/button/components/button/button.component.scss +0 -92
- package/src/lib/modules/button/components/button/button.component.ts +0 -67
- package/src/lib/modules/button-dropdown/button-dropdown.models.ts +0 -26
- package/src/lib/modules/button-dropdown/button-dropdown.module.ts +0 -22
- package/src/lib/modules/button-dropdown/components/button-dropdown/button-dropdown.component.html +0 -41
- package/src/lib/modules/button-dropdown/components/button-dropdown/button-dropdown.component.scss +0 -135
- package/src/lib/modules/button-dropdown/components/button-dropdown/button-dropdown.component.ts +0 -160
- package/src/lib/modules/configurable-form/component/configurable-form.component.html +0 -294
- package/src/lib/modules/configurable-form/component/configurable-form.component.scss +0 -503
- package/src/lib/modules/configurable-form/component/configurable-form.component.ts +0 -628
- package/src/lib/modules/configurable-form/configurable-form.examples.ts +0 -154
- package/src/lib/modules/configurable-form/configurable-form.model.ts +0 -131
- package/src/lib/modules/configurable-form/configurable-form.module.ts +0 -19
- package/src/lib/modules/confirmation-modal/components/confirmation-modal/confirmation-modal.component.html +0 -77
- package/src/lib/modules/confirmation-modal/components/confirmation-modal/confirmation-modal.component.scss +0 -395
- package/src/lib/modules/confirmation-modal/components/confirmation-modal/confirmation-modal.component.ts +0 -266
- package/src/lib/modules/confirmation-modal/confirmation-modal.models.ts +0 -71
- package/src/lib/modules/confirmation-modal/confirmation-modal.module.ts +0 -20
- package/src/lib/modules/filter/components/filter/filter.component.html +0 -131
- package/src/lib/modules/filter/components/filter/filter.component.scss +0 -245
- package/src/lib/modules/filter/components/filter/filter.component.ts +0 -216
- package/src/lib/modules/filter/filter.models.ts +0 -88
- package/src/lib/modules/filter/filter.module.ts +0 -24
- package/src/lib/modules/filter-sidebar/components/filter-sidebar/filter-sidebar.component.html +0 -112
- package/src/lib/modules/filter-sidebar/components/filter-sidebar/filter-sidebar.component.scss +0 -186
- package/src/lib/modules/filter-sidebar/components/filter-sidebar/filter-sidebar.component.ts +0 -163
- package/src/lib/modules/filter-sidebar/filter-sidebar.models.ts +0 -95
- package/src/lib/modules/filter-sidebar/filter-sidebar.module.ts +0 -24
- package/src/lib/modules/filter-table-selector/components/filter-table-selector/filter-table-selector.component.html +0 -73
- package/src/lib/modules/filter-table-selector/components/filter-table-selector/filter-table-selector.component.scss +0 -321
- package/src/lib/modules/filter-table-selector/components/filter-table-selector/filter-table-selector.component.ts +0 -361
- package/src/lib/modules/filter-table-selector/filter-table-selector.models.ts +0 -91
- package/src/lib/modules/filter-table-selector/filter-table-selector.module.ts +0 -22
- package/src/lib/modules/form-builder/components/field-configurator/configurator-config-panel/configurator-config-panel.component.html +0 -63
- package/src/lib/modules/form-builder/components/field-configurator/configurator-config-panel/configurator-config-panel.component.scss +0 -496
- package/src/lib/modules/form-builder/components/field-configurator/configurator-config-panel/configurator-config-panel.component.ts +0 -445
- package/src/lib/modules/form-builder/components/field-configurator/configurator-tree/configurator-tree.component.html +0 -75
- package/src/lib/modules/form-builder/components/field-configurator/configurator-tree/configurator-tree.component.scss +0 -210
- package/src/lib/modules/form-builder/components/field-configurator/configurator-tree/configurator-tree.component.ts +0 -55
- package/src/lib/modules/form-builder/components/field-configurator/field-configurator.component.html +0 -25
- package/src/lib/modules/form-builder/components/field-configurator/field-configurator.component.scss +0 -82
- package/src/lib/modules/form-builder/components/field-configurator/field-configurator.component.ts +0 -95
- package/src/lib/modules/form-builder/components/field-selection/field-selection.component.html +0 -20
- package/src/lib/modules/form-builder/components/field-selection/field-selection.component.scss +0 -37
- package/src/lib/modules/form-builder/components/field-selection/field-selection.component.ts +0 -94
- package/src/lib/modules/form-builder/components/field-selection/group-node/group-node.component.html +0 -46
- package/src/lib/modules/form-builder/components/field-selection/group-node/group-node.component.scss +0 -102
- package/src/lib/modules/form-builder/components/field-selection/group-node/group-node.component.ts +0 -50
- package/src/lib/modules/form-builder/components/field-selection/selection-field-node/selection-field-node.component.html +0 -35
- package/src/lib/modules/form-builder/components/field-selection/selection-field-node/selection-field-node.component.scss +0 -67
- package/src/lib/modules/form-builder/components/field-selection/selection-field-node/selection-field-node.component.ts +0 -34
- package/src/lib/modules/form-builder/components/field-selection/selection-section-node/selection-section-node.component.html +0 -68
- package/src/lib/modules/form-builder/components/field-selection/selection-section-node/selection-section-node.component.scss +0 -113
- package/src/lib/modules/form-builder/components/field-selection/selection-section-node/selection-section-node.component.ts +0 -74
- package/src/lib/modules/form-builder/configs/field-type-schema.map.ts +0 -533
- package/src/lib/modules/form-builder/form-builder.module.ts +0 -36
- package/src/lib/modules/form-builder/index.ts +0 -9
- package/src/lib/modules/form-builder/models/builder.models.ts +0 -7
- package/src/lib/modules/form-builder/models/field-configurator.models.ts +0 -38
- package/src/lib/modules/form-builder/models/field-selection.models.ts +0 -51
- package/src/lib/modules/form-builder/services/field-configurator.service.ts +0 -258
- package/src/lib/modules/form-builder/services/field-selection.service.ts +0 -299
- package/src/lib/modules/form-builder/services/form-schema-tree.service.ts +0 -670
- package/src/lib/modules/form-builder/tokens/builder.tokens.ts +0 -10
- package/src/lib/modules/form-builder/utils/constants.ts +0 -43
- package/src/lib/modules/form-components/components/checkbox/checkbox.component.html +0 -29
- package/src/lib/modules/form-components/components/checkbox/checkbox.component.scss +0 -111
- package/src/lib/modules/form-components/components/checkbox/checkbox.component.ts +0 -207
- package/src/lib/modules/form-components/components/checkbox/checkbox.models.ts +0 -35
- package/src/lib/modules/form-components/components/datepicker/datepicker.component.html +0 -42
- package/src/lib/modules/form-components/components/datepicker/datepicker.component.scss +0 -115
- package/src/lib/modules/form-components/components/datepicker/datepicker.component.ts +0 -267
- package/src/lib/modules/form-components/components/datepicker/datepicker.models.ts +0 -45
- package/src/lib/modules/form-components/components/dropdown/dropdown.component.html +0 -74
- package/src/lib/modules/form-components/components/dropdown/dropdown.component.scss +0 -252
- package/src/lib/modules/form-components/components/dropdown/dropdown.component.ts +0 -377
- package/src/lib/modules/form-components/components/dropdown/dropdown.models.ts +0 -53
- package/src/lib/modules/form-components/components/input/input.component.html +0 -51
- package/src/lib/modules/form-components/components/input/input.component.scss +0 -128
- package/src/lib/modules/form-components/components/input/input.component.ts +0 -250
- package/src/lib/modules/form-components/components/input/input.models.ts +0 -55
- package/src/lib/modules/form-components/components/radio/radio.component.html +0 -22
- package/src/lib/modules/form-components/components/radio/radio.component.scss +0 -107
- package/src/lib/modules/form-components/components/radio/radio.component.ts +0 -181
- package/src/lib/modules/form-components/components/radio/radio.models.ts +0 -39
- package/src/lib/modules/form-components/components/search/search.component.html +0 -15
- package/src/lib/modules/form-components/components/search/search.component.scss +0 -87
- package/src/lib/modules/form-components/components/search/search.component.ts +0 -213
- package/src/lib/modules/form-components/components/search/search.models.ts +0 -40
- package/src/lib/modules/form-components/components/toggle/toggle.component.html +0 -15
- package/src/lib/modules/form-components/components/toggle/toggle.component.scss +0 -81
- package/src/lib/modules/form-components/components/toggle/toggle.component.ts +0 -166
- package/src/lib/modules/form-components/components/toggle/toggle.models.ts +0 -27
- package/src/lib/modules/form-components/directives/click-outside.directive.ts +0 -22
- package/src/lib/modules/form-components/form-components.module.ts +0 -42
- package/src/lib/modules/form-field-configuration/components/_ffc-controls.scss +0 -89
- package/src/lib/modules/form-field-configuration/components/config-field-node/config-field-node.component.html +0 -68
- package/src/lib/modules/form-field-configuration/components/config-field-node/config-field-node.component.scss +0 -73
- package/src/lib/modules/form-field-configuration/components/config-field-node/config-field-node.component.ts +0 -34
- package/src/lib/modules/form-field-configuration/components/config-section-node/config-section-node.component.html +0 -78
- package/src/lib/modules/form-field-configuration/components/config-section-node/config-section-node.component.scss +0 -83
- package/src/lib/modules/form-field-configuration/components/config-section-node/config-section-node.component.ts +0 -94
- package/src/lib/modules/form-field-configuration/components/form-field-configuration/form-field-configuration.component.html +0 -38
- package/src/lib/modules/form-field-configuration/components/form-field-configuration/form-field-configuration.component.scss +0 -42
- package/src/lib/modules/form-field-configuration/components/form-field-configuration/form-field-configuration.component.ts +0 -92
- package/src/lib/modules/form-field-configuration/form-field-configuration.module.ts +0 -23
- package/src/lib/modules/form-field-configuration/index.ts +0 -7
- package/src/lib/modules/form-field-configuration/models/field-configuration.models.ts +0 -62
- package/src/lib/modules/form-field-configuration/services/config-schema-tree.service.ts +0 -159
- package/src/lib/modules/form-field-configuration/services/field-configuration.service.ts +0 -228
- package/src/lib/modules/material/material.module.ts +0 -94
- package/src/lib/modules/nav/components/nav/nav.component.html +0 -34
- package/src/lib/modules/nav/components/nav/nav.component.scss +0 -171
- package/src/lib/modules/nav/components/nav/nav.component.ts +0 -82
- package/src/lib/modules/nav/nav.models.ts +0 -31
- package/src/lib/modules/nav/nav.module.ts +0 -17
- package/src/lib/modules/pagination/components/pagination/pagination.component.html +0 -52
- package/src/lib/modules/pagination/components/pagination/pagination.component.scss +0 -155
- package/src/lib/modules/pagination/components/pagination/pagination.component.ts +0 -109
- package/src/lib/modules/pagination/pagination.module.ts +0 -17
- package/src/lib/modules/side-nav/components/side-nav/side-nav.component.html +0 -56
- package/src/lib/modules/side-nav/components/side-nav/side-nav.component.scss +0 -342
- package/src/lib/modules/side-nav/components/side-nav/side-nav.component.ts +0 -135
- package/src/lib/modules/side-nav/side-nav.models.ts +0 -38
- package/src/lib/modules/side-nav/side-nav.module.ts +0 -16
- package/src/lib/modules/smart-form/components/form-field/form-field.component.html +0 -1379
- package/src/lib/modules/smart-form/components/form-field/form-field.component.scss +0 -2255
- package/src/lib/modules/smart-form/components/form-field/form-field.component.ts +0 -3174
- package/src/lib/modules/smart-form/components/form-section/form-section.component.html +0 -67
- package/src/lib/modules/smart-form/components/form-section/form-section.component.scss +0 -209
- package/src/lib/modules/smart-form/components/form-section/form-section.component.ts +0 -142
- package/src/lib/modules/smart-form/components/smart-form/smart-form.component.html +0 -253
- package/src/lib/modules/smart-form/components/smart-form/smart-form.component.scss +0 -689
- package/src/lib/modules/smart-form/components/smart-form/smart-form.component.ts +0 -1134
- package/src/lib/modules/smart-form/index.ts +0 -10
- package/src/lib/modules/smart-form/models/form-schema.model.ts +0 -803
- package/src/lib/modules/smart-form/models/hierarchy-config.model.ts +0 -22
- package/src/lib/modules/smart-form/services/expression.service.ts +0 -75
- package/src/lib/modules/smart-form/services/smart-form-controller.service.ts +0 -67
- package/src/lib/modules/smart-form/smart-form.examples.ts +0 -1324
- package/src/lib/modules/smart-form/smart-form.module.ts +0 -40
- package/src/lib/modules/smart-form/utils/translation.utils.ts +0 -82
- package/src/lib/modules/smart-form/utils/trusted-url.pipe.ts +0 -25
- package/src/lib/modules/smart-form/utils/validation.utils.ts +0 -98
- package/src/lib/modules/smart-table/components/smart-table/smart-table.component.html +0 -283
- package/src/lib/modules/smart-table/components/smart-table/smart-table.component.scss +0 -685
- package/src/lib/modules/smart-table/components/smart-table/smart-table.component.ts +0 -1219
- package/src/lib/modules/smart-table/models/table-config.model.ts +0 -247
- package/src/lib/modules/smart-table/smart-table.module.ts +0 -30
- package/src/lib/modules/smart-table/utils/safe-html.pipe.ts +0 -28
- package/src/lib/modules/smart-table/utils/smart-table.utils.ts +0 -18
- package/src/lib/modules/snackbar/components/snackbar.component.html +0 -41
- package/src/lib/modules/snackbar/components/snackbar.component.scss +0 -99
- package/src/lib/modules/snackbar/components/snackbar.component.ts +0 -18
- package/src/lib/modules/snackbar/models/snackbar.models.ts +0 -10
- package/src/lib/modules/snackbar/services/snackbar.service.ts +0 -40
- package/src/lib/modules/snackbar/snackbar.module.ts +0 -11
- package/src/lib/modules/summary-card/components/summary-card/summary-card.component.html +0 -47
- package/src/lib/modules/summary-card/components/summary-card/summary-card.component.scss +0 -199
- package/src/lib/modules/summary-card/components/summary-card/summary-card.component.ts +0 -126
- package/src/lib/modules/summary-card/summary-card.module.ts +0 -18
- package/src/lib/modules/table-column-configuration/components/_tcc-controls.scss +0 -53
- package/src/lib/modules/table-column-configuration/components/config-column-node/config-column-node.component.html +0 -47
- package/src/lib/modules/table-column-configuration/components/config-column-node/config-column-node.component.scss +0 -50
- package/src/lib/modules/table-column-configuration/components/config-column-node/config-column-node.component.ts +0 -33
- package/src/lib/modules/table-column-configuration/components/table-column-configuration/table-column-configuration.component.html +0 -30
- package/src/lib/modules/table-column-configuration/components/table-column-configuration/table-column-configuration.component.scss +0 -36
- package/src/lib/modules/table-column-configuration/components/table-column-configuration/table-column-configuration.component.ts +0 -78
- package/src/lib/modules/table-column-configuration/index.ts +0 -5
- package/src/lib/modules/table-column-configuration/models/table-column-configuration.models.ts +0 -20
- package/src/lib/modules/table-column-configuration/services/table-column-configuration.service.ts +0 -77
- package/src/lib/modules/table-column-configuration/table-column-configuration.module.ts +0 -21
- package/src/lib/modules/time-picker/components/time-picker/time-picker.component.html +0 -37
- package/src/lib/modules/time-picker/components/time-picker/time-picker.component.scss +0 -102
- package/src/lib/modules/time-picker/components/time-picker/time-picker.component.ts +0 -178
- package/src/lib/modules/time-picker/components/time-wheel-panel/time-wheel-panel.component.html +0 -78
- package/src/lib/modules/time-picker/components/time-wheel-panel/time-wheel-panel.component.scss +0 -226
- package/src/lib/modules/time-picker/components/time-wheel-panel/time-wheel-panel.component.ts +0 -595
- package/src/lib/modules/time-picker/models/time-picker.models.ts +0 -49
- package/src/lib/modules/time-picker/time-picker.module.ts +0 -23
- package/src/lib/shared-ui.module.ts +0 -55
- package/src/lib/utils/constants.ts +0 -11
- package/src/lib/utils/storage.utils.ts +0 -37
- package/src/lib/utils/string.utils.ts +0 -23
- package/src/lib/utils/translation.utils.ts +0 -87
- package/src/public-api.ts +0 -127
- package/tsconfig.lib.json +0 -15
|
@@ -1,2819 +0,0 @@
|
|
|
1
|
-
# Smart Form Module
|
|
2
|
-
|
|
3
|
-
A powerful, JSON-driven dynamic form builder for Angular applications. This module allows you to create complex forms with conditional visibility, auto-calculated fields, multi-section support, stepper navigation, repeatable groups, column-based layouts, automatic API submission, and full i18n support — using simple JSON configurations.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Quick Start](#quick-start)
|
|
9
|
-
- [Form Types](#form-types)
|
|
10
|
-
- [Field Types](#field-types)
|
|
11
|
-
- [Layout System](#layout-system)
|
|
12
|
-
- [Configuration Options](#configuration-options)
|
|
13
|
-
- [Advanced Features](#advanced-features)
|
|
14
|
-
- [i18n / Translation Support](#i18n--translation-support)
|
|
15
|
-
- [API Submission](#api-submission)
|
|
16
|
-
- [API Authentication (Token)](#api-authentication-token)
|
|
17
|
-
- [Theme Configuration](#theme-configuration)
|
|
18
|
-
- [Examples](#examples)
|
|
19
|
-
- [API Reference](#api-reference)
|
|
20
|
-
- [Best Practices](#best-practices)
|
|
21
|
-
- [Troubleshooting](#troubleshooting)
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
26
|
-
|
|
27
|
-
To install the latest version of the `commons-shared-web-ui` library, run the following command:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
npm install commons-shared-web-ui@latest
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
1. Import the module in your Angular application:
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
import { SmartFormModule } from "./modules/smart-form/smart-form.module";
|
|
37
|
-
|
|
38
|
-
@NgModule({
|
|
39
|
-
imports: [SmartFormModule],
|
|
40
|
-
})
|
|
41
|
-
export class AppModule {}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## Quick Start
|
|
47
|
-
|
|
48
|
-
### Basic Usage
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
import { Component } from "@angular/core";
|
|
52
|
-
|
|
53
|
-
@Component({
|
|
54
|
-
selector: "app-example",
|
|
55
|
-
template: `
|
|
56
|
-
<lib-smart-form
|
|
57
|
-
[formJson]="formJson"
|
|
58
|
-
[initialValues]="initialValues"
|
|
59
|
-
[labels]="labels"
|
|
60
|
-
(submit)="onSubmit($event)"
|
|
61
|
-
>
|
|
62
|
-
</lib-smart-form>
|
|
63
|
-
`,
|
|
64
|
-
})
|
|
65
|
-
export class ExampleComponent {
|
|
66
|
-
formJson = JSON.stringify({
|
|
67
|
-
entityType: "USER",
|
|
68
|
-
label: "FORM.TITLE",
|
|
69
|
-
formType: "SECTION",
|
|
70
|
-
sectionConfig: {
|
|
71
|
-
children: [
|
|
72
|
-
{
|
|
73
|
-
name: "firstName",
|
|
74
|
-
label: "FIELD.FIRST_NAME",
|
|
75
|
-
type: "TEXT_INPUT",
|
|
76
|
-
subType: "SHORT",
|
|
77
|
-
required: true,
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: "email",
|
|
81
|
-
label: "FIELD.EMAIL",
|
|
82
|
-
type: "TEXT_INPUT",
|
|
83
|
-
subType: "EMAIL",
|
|
84
|
-
required: true,
|
|
85
|
-
},
|
|
86
|
-
],
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
initialValues = { firstName: "John" };
|
|
91
|
-
|
|
92
|
-
labels = {
|
|
93
|
-
"FORM.TITLE": "User Registration",
|
|
94
|
-
"FIELD.FIRST_NAME": "First Name",
|
|
95
|
-
"FIELD.EMAIL": "Email Address",
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
onSubmit(data: any) {
|
|
99
|
-
console.log("Form submitted:", data);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Form Types
|
|
107
|
-
|
|
108
|
-
### 1. Section Form (SECTION)
|
|
109
|
-
|
|
110
|
-
A simple form with all fields displayed at once.
|
|
111
|
-
|
|
112
|
-
```json
|
|
113
|
-
{
|
|
114
|
-
"entityType": "USER",
|
|
115
|
-
"label": "User Form",
|
|
116
|
-
"formType": "SECTION",
|
|
117
|
-
"sectionConfig": {
|
|
118
|
-
"children": [...]
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### 2. Stepper Form (STEPPER)
|
|
124
|
-
|
|
125
|
-
A multi-step form with navigation between steps. Each step is a `GROUP` field with a `sectionConfig`.
|
|
126
|
-
|
|
127
|
-
```json
|
|
128
|
-
{
|
|
129
|
-
"entityType": "USER",
|
|
130
|
-
"label": "User Registration",
|
|
131
|
-
"formType": "STEPPER",
|
|
132
|
-
"stepperConfig": {
|
|
133
|
-
"children": [
|
|
134
|
-
{
|
|
135
|
-
"type": "GROUP",
|
|
136
|
-
"subType": "SECTION",
|
|
137
|
-
"sectionConfig": {
|
|
138
|
-
"label": "Step 1: Basic Info",
|
|
139
|
-
"children": [...]
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"type": "GROUP",
|
|
144
|
-
"subType": "SECTION",
|
|
145
|
-
"sectionConfig": {
|
|
146
|
-
"label": "Step 2: Contact Info",
|
|
147
|
-
"children": [...]
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
],
|
|
151
|
-
"showStep": true,
|
|
152
|
-
"isHorizontal": true
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### 3. Section Stepper (SECTION + `sectionStepper`)
|
|
158
|
-
|
|
159
|
-
A `SECTION` form where top-level `GROUP` children are rendered as a horizontal step-progress bar at the top, showing one section at a time. Set `"sectionStepper": true` to enable it explicitly. The form auto-detects it when all top-level children are `GROUP` type — set `"sectionStepper": false` to suppress auto-detection.
|
|
160
|
-
|
|
161
|
-
> **Note:** A schema with exactly **one** surviving top-level `GROUP` (e.g. after a business user disables every other section via the Field Selection Configurator — see `form-builder.md`) is still treated as a section stepper. The step nav bar itself is hidden when there's only one step, so it renders as a plain single-section form instead of the blank result older versions produced in this case.
|
|
162
|
-
|
|
163
|
-
The component automatically renders **Previous** and **Next** buttons in the action bar. Any configured `actionBarConfig` buttons appear alongside these navigation buttons.
|
|
164
|
-
|
|
165
|
-
```json
|
|
166
|
-
{
|
|
167
|
-
"entityType": "REGISTRATION",
|
|
168
|
-
"label": "Multi-Step Registration",
|
|
169
|
-
"formType": "SECTION",
|
|
170
|
-
"sectionStepper": true,
|
|
171
|
-
"sectionConfig": {
|
|
172
|
-
"children": [
|
|
173
|
-
{
|
|
174
|
-
"type": "GROUP",
|
|
175
|
-
"subType": "SECTION",
|
|
176
|
-
"sectionConfig": {
|
|
177
|
-
"label": "Personal Info",
|
|
178
|
-
"children": [...]
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
"type": "GROUP",
|
|
183
|
-
"subType": "SECTION",
|
|
184
|
-
"sectionConfig": {
|
|
185
|
-
"label": "Contact Details",
|
|
186
|
-
"children": [...]
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
"type": "GROUP",
|
|
191
|
-
"subType": "SECTION",
|
|
192
|
-
"sectionConfig": {
|
|
193
|
-
"label": "Review & Submit",
|
|
194
|
-
"children": [...]
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
]
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
| Setting | Behaviour |
|
|
203
|
-
|---------|-----------|
|
|
204
|
-
| `"sectionStepper": true` | Always enables section stepper |
|
|
205
|
-
| `"sectionStepper": false` | Disables section stepper; renders all sections at once in the regular layout |
|
|
206
|
-
| Omitted | Auto-detected when all top-level children are `GROUP` type (one or more) |
|
|
207
|
-
|
|
208
|
-
### Host-Driven Navigation (Section Stepper `@ViewChild` API)
|
|
209
|
-
|
|
210
|
-
For layouts where the host app renders its **own** Previous/Next/Submit buttons (instead of relying on `actionBarConfig`), grab a reference to `SmartFormComponent` via `@ViewChild` and drive the stepper directly:
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
import { ViewChild } from '@angular/core';
|
|
214
|
-
import { SmartFormComponent } from 'commons-shared-web-ui';
|
|
215
|
-
|
|
216
|
-
export class MyWizardComponent {
|
|
217
|
-
@ViewChild(SmartFormComponent) smartFormComponent!: SmartFormComponent;
|
|
218
|
-
|
|
219
|
-
onStepChange(info: { currentStep: number; totalSteps: number; isFirst: boolean; isLast: boolean; stepLabel: string }) {
|
|
220
|
-
this.stepInfo = info; // drive your own footer UI from this
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
onNextClicked(): void {
|
|
224
|
-
// isCurrentStepValid() marks the current step's controls as touched and returns
|
|
225
|
-
// false if any required field on THIS step is invalid — navigateToNext() itself
|
|
226
|
-
// never blocks, it always advances (it only records a soft "warning" badge state).
|
|
227
|
-
if (this.smartFormComponent.isCurrentStepValid() === false) {
|
|
228
|
-
return; // field-level error states are now visible; stop here
|
|
229
|
-
}
|
|
230
|
-
this.smartFormComponent.navigateToNext();
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
```html
|
|
236
|
-
<lib-smart-form
|
|
237
|
-
[formJson]="formJson"
|
|
238
|
-
(stepChange)="onStepChange($event)">
|
|
239
|
-
</lib-smart-form>
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
| Member | Type | Description |
|
|
243
|
-
| :--- | :--- | :--- |
|
|
244
|
-
| `navigateToNext()` | method | Advances to the next section step. Marks the step being left with a `valid`/`warning` badge but **always advances** regardless of validity. |
|
|
245
|
-
| `navigateToPrevious()` | method | Goes back to the previous section step. Does not run validation. |
|
|
246
|
-
| `goToSectionStep(index)` | method | Jumps directly to a step by index (e.g. clicking a step nav bubble). Validates the step being left, same as `navigateToNext()`. |
|
|
247
|
-
| `isCurrentStepValid()` | method → `boolean` | **The hard-block guard.** Marks every control in the current step as touched (so field-level errors render) and returns `false` if any required field is invalid. Call this before `navigateToNext()` if your screen must prevent skipping incomplete steps — `navigateToNext()` alone will never do this for you. Returns `true` immediately for non-stepper forms. |
|
|
248
|
-
| `isSectionStepFirst` | getter → `boolean` | `true` when on the first step. |
|
|
249
|
-
| `isSectionStepLast` | getter → `boolean` | `true` when on the last step. For a single-step form (see note above), this and `isSectionStepFirst` are both `true` simultaneously. |
|
|
250
|
-
| `(stepChange)` | `EventEmitter<{ currentStep, totalSteps, isFirst, isLast, stepLabel }>` | Emitted after every step change — use it to sync a custom footer/progress UI. |
|
|
251
|
-
|
|
252
|
-
> **Important:** validation is opt-in at the host level. If you never call `isCurrentStepValid()`, users can freely skip past incomplete required fields by clicking Next — only the step's nav badge shows a warning. This is intentional: some hosts want a soft warning only, others (e.g. multi-step onboarding wizards) want a hard block. Add the `isCurrentStepValid()` check in your own Next handler only if you want the latter.
|
|
253
|
-
|
|
254
|
-
---
|
|
255
|
-
|
|
256
|
-
## Field Types
|
|
257
|
-
|
|
258
|
-
### 1. Text Input (TEXT_INPUT)
|
|
259
|
-
|
|
260
|
-
**SubTypes:** `SHORT`, `LONG`, `EMAIL`, `PHONE`, `PASSWORD`
|
|
261
|
-
|
|
262
|
-
```json
|
|
263
|
-
{
|
|
264
|
-
"name": "firstName",
|
|
265
|
-
"label": "First Name",
|
|
266
|
-
"type": "TEXT_INPUT",
|
|
267
|
-
"subType": "SHORT",
|
|
268
|
-
"required": true,
|
|
269
|
-
"hint": "Enter your first name",
|
|
270
|
-
"placeholder": "e.g. John",
|
|
271
|
-
"textConfig": {
|
|
272
|
-
"length": { "min": 2, "max": 50 },
|
|
273
|
-
"pattern": "^[A-Za-z ]+$",
|
|
274
|
-
"patternMessage": "Only alphabets and spaces are allowed"
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
**Password with match validation** (`subType: PASSWORD` + `matchField`):
|
|
280
|
-
|
|
281
|
-
```json
|
|
282
|
-
{
|
|
283
|
-
"name": "password",
|
|
284
|
-
"label": "Password",
|
|
285
|
-
"type": "TEXT_INPUT",
|
|
286
|
-
"subType": "PASSWORD",
|
|
287
|
-
"required": true,
|
|
288
|
-
"textConfig": { "length": { "min": 8, "max": 64 } }
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
"name": "confirmPassword",
|
|
292
|
-
"label": "Confirm Password",
|
|
293
|
-
"type": "TEXT_INPUT",
|
|
294
|
-
"subType": "PASSWORD",
|
|
295
|
-
"required": true,
|
|
296
|
-
"textConfig": { "matchField": "password" }
|
|
297
|
-
}
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
The `matchField` property tells the field to validate that its value equals the named sibling field. If the values differ, a `passwordMismatch` error is added and a user-friendly message is displayed.
|
|
301
|
-
|
|
302
|
-
### 2. Number Input (NUMBER_INPUT)
|
|
303
|
-
|
|
304
|
-
**SubTypes:** `INTEGER`, `DECIMAL`
|
|
305
|
-
|
|
306
|
-
```json
|
|
307
|
-
{
|
|
308
|
-
"name": "age",
|
|
309
|
-
"label": "Age",
|
|
310
|
-
"type": "NUMBER_INPUT",
|
|
311
|
-
"subType": "INTEGER",
|
|
312
|
-
"required": true,
|
|
313
|
-
"numberConfig": { "min": 18, "max": 100, "step": 1 }
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### 3. Date Input (DATE)
|
|
318
|
-
|
|
319
|
-
**SubTypes:** `SINGLE`
|
|
320
|
-
|
|
321
|
-
```json
|
|
322
|
-
{
|
|
323
|
-
"name": "birthDate",
|
|
324
|
-
"label": "Date of Birth",
|
|
325
|
-
"type": "DATE",
|
|
326
|
-
"subType": "SINGLE",
|
|
327
|
-
"required": true,
|
|
328
|
-
"dateConfig": {
|
|
329
|
-
"allowFuture": false,
|
|
330
|
-
"minDate": "1920-01-01",
|
|
331
|
-
"maxDate": "2010-12-31"
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
**`dateConfig` properties**
|
|
337
|
-
|
|
338
|
-
| Property | Type | Description |
|
|
339
|
-
|---|---|---|
|
|
340
|
-
| `allowFuture` | `boolean` | When `false`, dates after today are blocked (default: `true`) |
|
|
341
|
-
| `minDate` | `string` | Static minimum date in `YYYY-MM-DD` format |
|
|
342
|
-
| `maxDate` | `string` | Static maximum date in `YYYY-MM-DD` format |
|
|
343
|
-
| `minDateField` | `string` | Name of a sibling field whose current value is used as the dynamic minimum date. When the source field changes, the min date updates reactively. If the current field's value is now before the new minimum, it is automatically cleared. |
|
|
344
|
-
| `inputReadonly` | `boolean` | When `true`, the text input is read-only — the user can only pick a date from the calendar, not type one. Default: `false` |
|
|
345
|
-
|
|
346
|
-
**Dynamic min date (linked to another field)**
|
|
347
|
-
|
|
348
|
-
Use `minDateField` to link the minimum of an end-date picker to a start-date field:
|
|
349
|
-
|
|
350
|
-
```json
|
|
351
|
-
[
|
|
352
|
-
{
|
|
353
|
-
"name": "startDate",
|
|
354
|
-
"label": "Start Date",
|
|
355
|
-
"type": "DATE",
|
|
356
|
-
"subType": "SINGLE",
|
|
357
|
-
"required": true,
|
|
358
|
-
"dateConfig": { "inputReadonly": true }
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
"name": "endDate",
|
|
362
|
-
"label": "End Date",
|
|
363
|
-
"type": "DATE",
|
|
364
|
-
"subType": "SINGLE",
|
|
365
|
-
"required": true,
|
|
366
|
-
"dateConfig": {
|
|
367
|
-
"inputReadonly": true,
|
|
368
|
-
"minDateField": "startDate"
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
]
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
The End Date picker automatically sets its minimum to whatever value `startDate` holds. If the user later picks an earlier start date, `endDate` is cleared automatically.
|
|
375
|
-
|
|
376
|
-
### 4. Time Input (TIME)
|
|
377
|
-
|
|
378
|
-
**SubTypes:** `SINGLE`
|
|
379
|
-
|
|
380
|
-
```json
|
|
381
|
-
{
|
|
382
|
-
"name": "meetingTime",
|
|
383
|
-
"label": "Meeting Time",
|
|
384
|
-
"type": "TIME",
|
|
385
|
-
"subType": "SINGLE",
|
|
386
|
-
"required": true,
|
|
387
|
-
"placeholder": "Select Time"
|
|
388
|
-
}
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
**`timeConfig` options** (all optional):
|
|
392
|
-
|
|
393
|
-
| Key | Type | Description |
|
|
394
|
-
| --- | --- | --- |
|
|
395
|
-
| `minTime` | string | Explicit minimum time in 24-hour `"HH:mm"` format (e.g. `"09:00"`). |
|
|
396
|
-
| `maxTime` | string | Explicit maximum time in 24-hour `"HH:mm"` format (e.g. `"18:00"`). |
|
|
397
|
-
| `inputReadonly` | boolean | When true, the input is readonly. |
|
|
398
|
-
| `minTimeField` | string | Name of a sibling TIME field whose value is used as the dynamic minimum. When it changes, this field's minimum updates and any now-invalid value (earlier than the new minimum) is cleared. Mirrors `dateConfig.minTimeField`. |
|
|
399
|
-
| `variant` | string | Rendering variant: `'default'` (native picker input, default) or `'wheel'` (custom drum-roll slide picker). |
|
|
400
|
-
| `mode` | string | Hour display mode for `'wheel'` variant: `'12'` (12-hour, AM/PM column, default) or `'24'` (24-hour, no AM/PM). |
|
|
401
|
-
| `minuteStep` | number | Minute column increment step for `'wheel'` variant. Omit (or `1`) for every minute `00–59`; a value greater than `0` adds the break (e.g. `5`, `10`, `15`, `30`). |
|
|
402
|
-
| `placeholder` | string | Overridden placeholder text for the `'wheel'` variant trigger field. |
|
|
403
|
-
|
|
404
|
-
**Start / End time pair** — the end time cannot be earlier than the start time (same pattern as `dateConfig.minDateField`):
|
|
405
|
-
|
|
406
|
-
```json
|
|
407
|
-
{
|
|
408
|
-
"name": "startTime",
|
|
409
|
-
"label": "Start Time",
|
|
410
|
-
"type": "TIME",
|
|
411
|
-
"required": true,
|
|
412
|
-
"colSpan": 2
|
|
413
|
-
},
|
|
414
|
-
{
|
|
415
|
-
"name": "endTime",
|
|
416
|
-
"label": "End Time",
|
|
417
|
-
"type": "TIME",
|
|
418
|
-
"required": true,
|
|
419
|
-
"colSpan": 2,
|
|
420
|
-
"timeConfig": {
|
|
421
|
-
"minTimeField": "startTime"
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
**Custom Wheel Time Picker Example** — using the custom drum-roll picker with 12-hour mode and 15-minute increments:
|
|
427
|
-
|
|
428
|
-
```json
|
|
429
|
-
{
|
|
430
|
-
"name": "preferredSlot",
|
|
431
|
-
"label": "Preferred Meeting Slot",
|
|
432
|
-
"type": "TIME",
|
|
433
|
-
"subType": "SINGLE",
|
|
434
|
-
"required": true,
|
|
435
|
-
"timeConfig": {
|
|
436
|
-
"variant": "wheel",
|
|
437
|
-
"mode": "12",
|
|
438
|
-
"minuteStep": 15,
|
|
439
|
-
"minTime": "09:00",
|
|
440
|
-
"maxTime": "17:00",
|
|
441
|
-
"placeholder": "Choose a 15-minute slot"
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
> **Note:** `minuteStep` omitted (or `0`/`1`) shows every minute `00–59`; any value greater than `0` adds the step break (e.g. `15` → `00, 15, 30, 45`). The wheel opens as a compact, centered modal.
|
|
447
|
-
|
|
448
|
-
**Styling & Variants (SCSS)**:
|
|
449
|
-
By default, the time picker uses the black/grey theme (Image 2 style). To use a blue accent variant instead, include the theme mixin with overrides inside your global stylesheet or MFE container styles:
|
|
450
|
-
|
|
451
|
-
```scss
|
|
452
|
-
@use 'commons-shared-web-ui/lib/modules/time-picker/time-picker.theme' as tp;
|
|
453
|
-
|
|
454
|
-
// Default variant (Black/Grey Color Scheme)
|
|
455
|
-
:root {
|
|
456
|
-
@include tp.time-picker-theme();
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Blue Accent Variant
|
|
460
|
-
:root {
|
|
461
|
-
@include tp.time-picker-theme((
|
|
462
|
-
accent: #4D89EB,
|
|
463
|
-
accent-light: rgba(77, 137, 235, 0.08),
|
|
464
|
-
accent-border: rgba(77, 137, 235, 0.15),
|
|
465
|
-
btn-confirm-bg: #4D89EB,
|
|
466
|
-
btn-confirm-hover: #3b74d1,
|
|
467
|
-
btn-cancel-bg: transparent,
|
|
468
|
-
btn-cancel-border: #e5e7eb,
|
|
469
|
-
btn-cancel-hover: #e5e7eb
|
|
470
|
-
));
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
**Time Picker i18n labels** — the `'wheel'` variant reads these keys from the form-level `[labels]` map (all optional; each falls back to English):
|
|
475
|
-
|
|
476
|
-
| Label key | Applies to | Default |
|
|
477
|
-
| --- | --- | --- |
|
|
478
|
-
| `TIME_PICKER.SELECT_TIME` | Popup title | `Select Time` |
|
|
479
|
-
| `TIME_PICKER.HOUR` | Hour column header | `Hour` |
|
|
480
|
-
| `TIME_PICKER.MINUTE` | Minute column header | `Minute` |
|
|
481
|
-
| `TIME_PICKER.PERIOD` | AM/PM column header | `AM/PM` |
|
|
482
|
-
| `TIME_PICKER.AM` / `TIME_PICKER.PM` | Period values | `AM` / `PM` |
|
|
483
|
-
| `COMMON.ACTIONS.CONFIRM` | Confirm button | `Confirm` |
|
|
484
|
-
| `COMMON.ACTIONS.CANCEL` | Cancel button | `Cancel` |
|
|
485
|
-
|
|
486
|
-
#### Complete Example (TIME wheel field in a Smart Form)
|
|
487
|
-
|
|
488
|
-
A full form schema with a start/end wheel time-picker pair, plus the parent-component wiring and picker i18n labels.
|
|
489
|
-
|
|
490
|
-
```typescript
|
|
491
|
-
import { Component } from '@angular/core';
|
|
492
|
-
|
|
493
|
-
@Component({
|
|
494
|
-
selector: 'app-booking-form',
|
|
495
|
-
template: `
|
|
496
|
-
<lib-smart-form
|
|
497
|
-
[formJson]="formJson"
|
|
498
|
-
[labels]="labels"
|
|
499
|
-
(submit)="onSubmit($event)">
|
|
500
|
-
</lib-smart-form>
|
|
501
|
-
`
|
|
502
|
-
})
|
|
503
|
-
export class BookingFormComponent {
|
|
504
|
-
formJson = JSON.stringify({
|
|
505
|
-
entityType: 'BOOKING',
|
|
506
|
-
label: 'FORM.BOOKING_TITLE',
|
|
507
|
-
formType: 'SECTION',
|
|
508
|
-
sectionConfig: {
|
|
509
|
-
children: [
|
|
510
|
-
{
|
|
511
|
-
type: 'ROW',
|
|
512
|
-
subType: 'HORIZONTAL',
|
|
513
|
-
children: [
|
|
514
|
-
{
|
|
515
|
-
name: 'startTime',
|
|
516
|
-
label: 'FIELD.START_TIME',
|
|
517
|
-
type: 'TIME',
|
|
518
|
-
subType: 'SINGLE',
|
|
519
|
-
required: true,
|
|
520
|
-
colSpan: 6,
|
|
521
|
-
timeConfig: {
|
|
522
|
-
variant: 'wheel',
|
|
523
|
-
mode: '12',
|
|
524
|
-
minuteStep: 15,
|
|
525
|
-
minTime: '09:00',
|
|
526
|
-
maxTime: '18:00'
|
|
527
|
-
}
|
|
528
|
-
},
|
|
529
|
-
{
|
|
530
|
-
name: 'endTime',
|
|
531
|
-
label: 'FIELD.END_TIME',
|
|
532
|
-
type: 'TIME',
|
|
533
|
-
subType: 'SINGLE',
|
|
534
|
-
required: true,
|
|
535
|
-
colSpan: 6,
|
|
536
|
-
timeConfig: {
|
|
537
|
-
variant: 'wheel',
|
|
538
|
-
mode: '12',
|
|
539
|
-
minuteStep: 15,
|
|
540
|
-
minTimeField: 'startTime' // end cannot precede start
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
]
|
|
544
|
-
}
|
|
545
|
-
]
|
|
546
|
-
},
|
|
547
|
-
submitConfig: {
|
|
548
|
-
apiUrl: 'https://api.example.com/bookings',
|
|
549
|
-
method: 'POST',
|
|
550
|
-
successMessage: 'Booking created!'
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
labels = {
|
|
555
|
-
'FORM.BOOKING_TITLE': 'New Booking',
|
|
556
|
-
'FIELD.START_TIME': 'Start Time',
|
|
557
|
-
'FIELD.END_TIME': 'End Time',
|
|
558
|
-
// Time picker popup i18n
|
|
559
|
-
'TIME_PICKER.SELECT_TIME': 'Pick a time',
|
|
560
|
-
'TIME_PICKER.HOUR': 'Hour',
|
|
561
|
-
'TIME_PICKER.MINUTE': 'Min',
|
|
562
|
-
'TIME_PICKER.PERIOD': 'AM/PM',
|
|
563
|
-
'COMMON.ACTIONS.CONFIRM': 'Confirm',
|
|
564
|
-
'COMMON.ACTIONS.CANCEL': 'Cancel'
|
|
565
|
-
};
|
|
566
|
-
|
|
567
|
-
onSubmit(payload: any): void {
|
|
568
|
-
// { startTime: "09:15 AM", endTime: "10:00 AM" }
|
|
569
|
-
console.log('Booking:', payload);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
### 5. Autocomplete (AUTOCOMPLETE)
|
|
575
|
-
|
|
576
|
-
A searchable input backed by Angular Material's `mat-autocomplete`. As the user types, the option list is filtered by label or code. The form control stores the **code** value, while the input shows the human-readable **label**.
|
|
577
|
-
|
|
578
|
-
Autocomplete fields now use a dedicated `autocompleteConfig` for specialized behavior, while still using `optionConfig` for core API/data settings.
|
|
579
|
-
|
|
580
|
-
#### Basic Usage (Local Filtering)
|
|
581
|
-
|
|
582
|
-
```json
|
|
583
|
-
{
|
|
584
|
-
"name": "country",
|
|
585
|
-
"label": "Country",
|
|
586
|
-
"type": "AUTOCOMPLETE",
|
|
587
|
-
"subType": "SINGLE",
|
|
588
|
-
"optionConfig": {
|
|
589
|
-
"optionList": [
|
|
590
|
-
{ "label": "India", "code": "IN" },
|
|
591
|
-
{ "label": "USA", "code": "US" }
|
|
592
|
-
]
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
#### Advanced Usage (Server-side Search & Rich Display)
|
|
598
|
-
|
|
599
|
-
For large datasets, use `autocompleteConfig` to trigger server-side filtering and display rich metadata (emails, phones, avatars) in the dropdown options.
|
|
600
|
-
|
|
601
|
-
```json
|
|
602
|
-
{
|
|
603
|
-
"name": "responsiblePerson",
|
|
604
|
-
"label": "Responsible Person",
|
|
605
|
-
"type": "AUTOCOMPLETE",
|
|
606
|
-
"optionConfig": {
|
|
607
|
-
"apiUrl": "gateway/search-service/api/v1/users",
|
|
608
|
-
"dataPath": "elements",
|
|
609
|
-
"labelPath": "displayName",
|
|
610
|
-
"valuePath": "userId"
|
|
611
|
-
},
|
|
612
|
-
"autocompleteConfig": {
|
|
613
|
-
"method": "POST",
|
|
614
|
-
"body": [],
|
|
615
|
-
"searchParam": "searchTerm",
|
|
616
|
-
"searchMinLength": 2,
|
|
617
|
-
"displayFields": [
|
|
618
|
-
{ "path": "photoUrl", "type": "image" },
|
|
619
|
-
{ "path": "login", "type": "email", "label": "Email: " },
|
|
620
|
-
{ "path": "phone", "type": "phone" }
|
|
621
|
-
]
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
```
|
|
625
|
-
|
|
626
|
-
#### Autocomplete Configuration (`autocompleteConfig`)
|
|
627
|
-
|
|
628
|
-
| Property | Type | Description |
|
|
629
|
-
| :--- | :--- | :--- |
|
|
630
|
-
| `method` | `'GET' \| 'POST' \| 'PUT' \| 'PATCH'` | HTTP method for the search API (Default: `GET`). |
|
|
631
|
-
| `body` | `any` | Static request body for `POST`/`PUT`/`PATCH` requests. |
|
|
632
|
-
| `labelTemplate` | `string` | Template string for composite labels, e.g., `"{firstName} {lastName} ({empId})"`. |
|
|
633
|
-
| `displayFields` | `string \| DisplayField[]` | Extra fields to show below the label. See [Rich Display Fields](#rich-display-fields). |
|
|
634
|
-
| `searchParam` | `string` | Query parameter key sent to the API as the user types (e.g., `"q"` or `"searchTerm"`). |
|
|
635
|
-
| `searchMinLength`| `number` | Minimum characters to type before firing an API request (Default: `1`). |
|
|
636
|
-
| `searchDebounce` | `number` | Delay in ms before firing the search request (Default: `300ms`). |
|
|
637
|
-
|
|
638
|
-
#### Rich Display Fields
|
|
639
|
-
|
|
640
|
-
The `displayFields` property allows you to show extra information in the dropdown. Each field is rendered with an appropriate icon and style.
|
|
641
|
-
|
|
642
|
-
| Type | Rendering |
|
|
643
|
-
| :--- | :--- |
|
|
644
|
-
| `text` | Plain secondary text (Default). |
|
|
645
|
-
| `email` | Rendered with an envelope icon in a pill-style chip. |
|
|
646
|
-
| `phone` | Rendered with a phone icon in a pill-style chip. |
|
|
647
|
-
| `image` | Rendered as a small circular avatar/thumbnail. |
|
|
648
|
-
|
|
649
|
-
#### Autocomplete Behaviour
|
|
650
|
-
|
|
651
|
-
| Feature | Detail |
|
|
652
|
-
| :--- | :--- |
|
|
653
|
-
| **Debouncing** | Automatically waits for the user to stop typing before calling the API. |
|
|
654
|
-
| **Syncing** | If the form is patched with a `code`, the component automatically fetches or resolves the `label` for display. |
|
|
655
|
-
| **Edge Case Handling** | If a user types an invalid value and blurs, the input reverts to the last valid selection. |
|
|
656
|
-
| **Clear Button** | A built-in "X" button allows users to reset the field instantly. |
|
|
657
|
-
|
|
658
|
-
### 4. Dropdown (DROPDOWN)
|
|
659
|
-
|
|
660
|
-
**SubTypes:** `SINGLE`, `MULTIPLE`
|
|
661
|
-
|
|
662
|
-
**Static options:**
|
|
663
|
-
|
|
664
|
-
```json
|
|
665
|
-
{
|
|
666
|
-
"name": "country",
|
|
667
|
-
"label": "Country",
|
|
668
|
-
"type": "DROPDOWN",
|
|
669
|
-
"subType": "SINGLE",
|
|
670
|
-
"required": true,
|
|
671
|
-
"optionConfig": {
|
|
672
|
-
"optionList": [
|
|
673
|
-
{ "label": "USA", "code": "US" },
|
|
674
|
-
{ "label": "Canada", "code": "CA" }
|
|
675
|
-
]
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
**Load from API** (supports nested data paths):
|
|
681
|
-
|
|
682
|
-
```json
|
|
683
|
-
{
|
|
684
|
-
"name": "department",
|
|
685
|
-
"label": "Department",
|
|
686
|
-
"type": "DROPDOWN",
|
|
687
|
-
"subType": "SINGLE",
|
|
688
|
-
"optionConfig": {
|
|
689
|
-
"apiUrl": "https://api.example.com/departments",
|
|
690
|
-
"dataPath": "data.items",
|
|
691
|
-
"labelPath": "profile.name",
|
|
692
|
-
"valuePath": "meta.id"
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
```
|
|
696
|
-
|
|
697
|
-
**Dependent Dropdown (Cascading Select):**
|
|
698
|
-
|
|
699
|
-
```json
|
|
700
|
-
{
|
|
701
|
-
"name": "state",
|
|
702
|
-
"label": "State",
|
|
703
|
-
"type": "DROPDOWN",
|
|
704
|
-
"subType": "SINGLE",
|
|
705
|
-
"optionConfig": {
|
|
706
|
-
"apiUrl": "https://api.example.com/states",
|
|
707
|
-
"valuePath": "code"
|
|
708
|
-
}
|
|
709
|
-
},
|
|
710
|
-
{
|
|
711
|
-
"name": "district",
|
|
712
|
-
"label": "District",
|
|
713
|
-
"type": "DROPDOWN",
|
|
714
|
-
"subType": "SINGLE",
|
|
715
|
-
"optionConfig": {
|
|
716
|
-
"apiUrl": "https://api.example.com/districts",
|
|
717
|
-
"dependencies": {
|
|
718
|
-
"stateCode": "state"
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
`dependencies` maps API query-param keys to other field names. The parent field's current value is automatically appended to the API URL.
|
|
725
|
-
|
|
726
|
-
**Merge Multiple APIs:**
|
|
727
|
-
|
|
728
|
-
```json
|
|
729
|
-
{
|
|
730
|
-
"name": "region",
|
|
731
|
-
"label": "Region",
|
|
732
|
-
"type": "DROPDOWN",
|
|
733
|
-
"subType": "SINGLE",
|
|
734
|
-
"optionConfig": {
|
|
735
|
-
"apiUrls": [
|
|
736
|
-
"https://api.example.com/states",
|
|
737
|
-
"https://api.example.com/union-territories"
|
|
738
|
-
],
|
|
739
|
-
"valuePath": "code",
|
|
740
|
-
"labelPath": "name"
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
**Sort Options:**
|
|
746
|
-
|
|
747
|
-
```json
|
|
748
|
-
{
|
|
749
|
-
"name": "country",
|
|
750
|
-
"label": "Select Country",
|
|
751
|
-
"type": "DROPDOWN",
|
|
752
|
-
"subType": "SINGLE",
|
|
753
|
-
"optionConfig": {
|
|
754
|
-
"apiUrl": "https://api.example.com/countries",
|
|
755
|
-
"valuePath": "code",
|
|
756
|
-
"labelPath": "name.common",
|
|
757
|
-
"sortBy": "name.common",
|
|
758
|
-
"sortDirection": "ASC"
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
**Searchable Dropdown (Local Filtering):**
|
|
764
|
-
|
|
765
|
-
Add a search input inside the dropdown panel to filter options client-side. Works with both `SINGLE` and `MULTIPLE` subtypes.
|
|
766
|
-
|
|
767
|
-
```json
|
|
768
|
-
{
|
|
769
|
-
"name": "country",
|
|
770
|
-
"label": "Select Country",
|
|
771
|
-
"type": "DROPDOWN",
|
|
772
|
-
"subType": "SINGLE",
|
|
773
|
-
"optionConfig": {
|
|
774
|
-
"apiUrl": "https://api.example.com/countries",
|
|
775
|
-
"valuePath": "code",
|
|
776
|
-
"labelPath": "name",
|
|
777
|
-
"searchConfig": {
|
|
778
|
-
"enabled": true
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
```
|
|
783
|
-
|
|
784
|
-
**Searchable Dropdown (Server-side Filtering via GET):**
|
|
785
|
-
|
|
786
|
-
For large datasets, configure the dropdown to query the API as the user types. The search term is appended as a query parameter to the existing API URL.
|
|
787
|
-
|
|
788
|
-
```json
|
|
789
|
-
{
|
|
790
|
-
"name": "academicSubject",
|
|
791
|
-
"label": "Academic Subject",
|
|
792
|
-
"type": "DROPDOWN",
|
|
793
|
-
"subType": "MULTIPLE",
|
|
794
|
-
"optionConfig": {
|
|
795
|
-
"apiUrl": "https://api.example.com/subjects",
|
|
796
|
-
"dataPath": "elements",
|
|
797
|
-
"labelPath": "name",
|
|
798
|
-
"valuePath": "code",
|
|
799
|
-
"searchConfig": {
|
|
800
|
-
"enabled": true,
|
|
801
|
-
"mode": "server",
|
|
802
|
-
"searchKey": "searchTerm",
|
|
803
|
-
"minSearchLength": 3,
|
|
804
|
-
"debounceTime": 400
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
```
|
|
809
|
-
|
|
810
|
-
**Loading Spinner (`showLoader`):**
|
|
811
|
-
|
|
812
|
-
To display an inline spinning loader during API option requests (e.g. for dynamic or dependent dropdowns), set `"showLoader": true` inside `optionConfig`. This works for both custom searchable dropdowns (`.mini-spinner`) and native single-select dropdowns (`.native-select-spinner`).
|
|
813
|
-
|
|
814
|
-
```json
|
|
815
|
-
{
|
|
816
|
-
"name": "district",
|
|
817
|
-
"label": "District",
|
|
818
|
-
"type": "DROPDOWN",
|
|
819
|
-
"subType": "SINGLE",
|
|
820
|
-
"optionConfig": {
|
|
821
|
-
"apiUrl": "https://api.example.com/districts",
|
|
822
|
-
"dataPath": "elements",
|
|
823
|
-
"showLoader": true
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
```
|
|
827
|
-
|
|
828
|
-
> **Native Select Loader Note:** For non-searchable single-select dropdowns (`config.subType === 'SINGLE' && !isSearchableDropdown`), the library wraps the native `<select>` element and overlays an absolute spinner (`.native-select-spinner`) positioned at `right: 2.5rem` with `pointer-events: none`, ensuring that the native browser dropdown clicks remain fully accessible while visually feedbacking options loading.
|
|
829
|
-
|
|
830
|
-
**Selected Option Label Persistence (Server-side Search):**
|
|
831
|
-
|
|
832
|
-
When using `mode: "server"`, the dropdown only displays options fetched from the API (such as the first page of results or matching search results). If a form is loaded in `EDIT` mode, the selected value might not be in the initial page of options, causing the field to appear blank in the UI.
|
|
833
|
-
|
|
834
|
-
To prevent this, you can pass the selected options' metadata (label, code, details) to `<lib-smart-form>` using the `selectedOptionsData` input property. This caches the selected item details and ensures they are merged into the dropdown's option list even after searches or paginated listings are fetched.
|
|
835
|
-
|
|
836
|
-
#### Example MFE Integration
|
|
837
|
-
|
|
838
|
-
##### 1. Form Schema Config (JSON)
|
|
839
|
-
```json
|
|
840
|
-
{
|
|
841
|
-
"name": "academicSubject",
|
|
842
|
-
"label": "Academic Subject",
|
|
843
|
-
"type": "DROPDOWN",
|
|
844
|
-
"subType": "SINGLE",
|
|
845
|
-
"optionConfig": {
|
|
846
|
-
"apiUrl": "https://api.example.com/subjects",
|
|
847
|
-
"valuePath": "id",
|
|
848
|
-
"labelPath": "name",
|
|
849
|
-
"searchConfig": {
|
|
850
|
-
"enabled": true,
|
|
851
|
-
"mode": "server",
|
|
852
|
-
"searchKey": "q"
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
```
|
|
857
|
-
|
|
858
|
-
##### 2. MFE Parent Component Markup
|
|
859
|
-
```html
|
|
860
|
-
<lib-smart-form
|
|
861
|
-
[formJson]="formSchema"
|
|
862
|
-
[initialValues]="formInitialValues"
|
|
863
|
-
[selectedOptionsData]="selectedOptionsData"
|
|
864
|
-
[mode]="'EDIT'"
|
|
865
|
-
(submit)="onSubmit($event)">
|
|
866
|
-
</lib-smart-form>
|
|
867
|
-
```
|
|
868
|
-
|
|
869
|
-
##### 3. MFE Parent Component Controller (TypeScript)
|
|
870
|
-
```typescript
|
|
871
|
-
import { Component, OnInit } from '@angular/core';
|
|
872
|
-
|
|
873
|
-
@Component({
|
|
874
|
-
selector: 'app-subject-edit',
|
|
875
|
-
templateUrl: './subject-edit.component.html'
|
|
876
|
-
})
|
|
877
|
-
export class SubjectEditComponent implements OnInit {
|
|
878
|
-
formSchema = `...`; // Schema JSON from Step 1
|
|
879
|
-
|
|
880
|
-
// 1. Keep initialValues flat with primitive codes/IDs
|
|
881
|
-
formInitialValues = {
|
|
882
|
-
academicSubject: 8
|
|
883
|
-
};
|
|
884
|
-
|
|
885
|
-
// 2. Provide the full metadata for the selected options so the form can display the label
|
|
886
|
-
selectedOptionsData = {
|
|
887
|
-
// For single select: pass a single object
|
|
888
|
-
academicSubject: {
|
|
889
|
-
id: 8,
|
|
890
|
-
name: "Chemistry"
|
|
891
|
-
}
|
|
892
|
-
// For multi-select: pass an array of objects
|
|
893
|
-
// academicSubjects: [
|
|
894
|
-
// { id: 8, name: "Chemistry" },
|
|
895
|
-
// { id: 10, name: "Mathematics" }
|
|
896
|
-
// ]
|
|
897
|
-
};
|
|
898
|
-
|
|
899
|
-
onSubmit(payload: any) {
|
|
900
|
-
// Submits the standard flat form payload containing raw codes:
|
|
901
|
-
// payload: { academicSubject: 8 }
|
|
902
|
-
console.log('Form Submitted:', payload);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
**Dropdown Caching & Redundant API Call Optimization:**
|
|
908
|
-
|
|
909
|
-
To prevent redundant API queries to the backend during user interaction, the Dropdown component caches fetched options in `localOptionList`.
|
|
910
|
-
|
|
911
|
-
1. **Overlay Opening**: When the dropdown overlay panel opens, the component triggers the API query *only* if the `localOptionList` is currently empty. If options have already been fetched, it loads options instantly from the cache, avoiding subsequent duplicate network calls.
|
|
912
|
-
2. **Overlay Closing**: When closing the dropdown (e.g. user selects an option, clicks outside, or hits the Escape key), the search input filter is cleared locally by passing `false` to `resetDropdownSearch(false)`. This clears the text search box but bypasses the option-loading API request completely.
|
|
913
|
-
3. **Typing and Filtering**: When the user explicitly types in the search input box, the component automatically bypasses the cache check and queries the backend with the search query parameters (`searchKey` / `searchString`).
|
|
914
|
-
4. **Dependency-Based Cache Invalidation**: If parent dependency fields change (e.g., changing the Academic Subject which invalidates the Learning Outcomes options), the dropdown cache is cleared reactively (`localOptionList = []`), forcing a fresh backend query the next time the dropdown is opened.
|
|
915
|
-
|
|
916
|
-
##### Example Searchable Server-Side Dropdown Config (JSON)
|
|
917
|
-
```json
|
|
918
|
-
{
|
|
919
|
-
"name": "academicSubject",
|
|
920
|
-
"label": "Academic Subject",
|
|
921
|
-
"type": "DROPDOWN",
|
|
922
|
-
"subType": "SINGLE",
|
|
923
|
-
"optionConfig": {
|
|
924
|
-
"apiUrl": "gateway/commons-mdm-service/api/v1/subjects",
|
|
925
|
-
"valuePath": "id",
|
|
926
|
-
"labelPath": "name[0].text",
|
|
927
|
-
"searchConfig": {
|
|
928
|
-
"enabled": true,
|
|
929
|
-
"mode": "server",
|
|
930
|
-
"searchKey": "searchTerm",
|
|
931
|
-
"minSearchLength": 3,
|
|
932
|
-
"debounceTime": 400
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
```
|
|
937
|
-
|
|
938
|
-
**Select All (Multi-Select Only):**
|
|
939
|
-
|
|
940
|
-
For `MULTIPLE` dropdowns, add a "Select All" checkbox at the top of the option list. Can be combined with `searchConfig` — when a search filter is active, "Select All" toggles only the visible (filtered) options.
|
|
941
|
-
|
|
942
|
-
```json
|
|
943
|
-
{
|
|
944
|
-
"name": "skills",
|
|
945
|
-
"label": "Skills",
|
|
946
|
-
"type": "DROPDOWN",
|
|
947
|
-
"subType": "MULTIPLE",
|
|
948
|
-
"optionConfig": {
|
|
949
|
-
"optionList": [
|
|
950
|
-
{ "label": "JavaScript", "code": "JS" },
|
|
951
|
-
{ "label": "Python", "code": "PY" },
|
|
952
|
-
{ "label": "Java", "code": "JAVA" },
|
|
953
|
-
{ "label": "Go", "code": "GO" }
|
|
954
|
-
],
|
|
955
|
-
"showSelectAll": true,
|
|
956
|
-
"searchConfig": {
|
|
957
|
-
"enabled": true
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
```
|
|
962
|
-
|
|
963
|
-
#### Dropdown Search Configuration (`searchConfig`)
|
|
964
|
-
|
|
965
|
-
| Property | Type | Default | Description |
|
|
966
|
-
| :--- | :--- | :--- | :--- |
|
|
967
|
-
| `enabled` | `boolean` | — | **Required.** Displays a search input inside the dropdown panel. |
|
|
968
|
-
| `mode` | `'local' \| 'server'` | `'local'` | `local`: filters client-side. `server`: queries the API on typing via GET. |
|
|
969
|
-
| `searchKey` | `string` | `'search'` | Query parameter key for the search term sent to the API. Only used in `server` mode. |
|
|
970
|
-
| `minSearchLength` | `number` | `3` | Minimum characters required before triggering a server-side search. Only used in `server` mode. |
|
|
971
|
-
| `debounceTime` | `number` | `300` | Debounce delay in milliseconds before firing the search. |
|
|
972
|
-
|
|
973
|
-
#### Select All Configuration
|
|
974
|
-
|
|
975
|
-
| Property | Type | Default | Description |
|
|
976
|
-
| :--- | :--- | :--- | :--- |
|
|
977
|
-
| `showSelectAll` | `boolean` | `false` | Renders a "Select All" checkbox at the top of a `MULTIPLE` select dropdown. |
|
|
978
|
-
|
|
979
|
-
#### i18n Labels for Search & Select All
|
|
980
|
-
|
|
981
|
-
The following label keys can be provided via the `labels` input to customise the UI text:
|
|
982
|
-
|
|
983
|
-
| Key | Default | Description |
|
|
984
|
-
| :--- | :--- | :--- |
|
|
985
|
-
| `SEARCH_PLACEHOLDER` | `Search...` | Placeholder text for the search input. |
|
|
986
|
-
| `SELECT_ALL` | `Select All` | Label for the Select All checkbox. |
|
|
987
|
-
| `NO_MATCHING_OPTIONS` | `No options match your search` | Shown when the search filter yields no results. |
|
|
988
|
-
|
|
989
|
-
### 5. Radio Button (RADIO)
|
|
990
|
-
|
|
991
|
-
```json
|
|
992
|
-
{
|
|
993
|
-
"name": "gender",
|
|
994
|
-
"label": "Gender",
|
|
995
|
-
"type": "RADIO",
|
|
996
|
-
"subType": "SINGLE",
|
|
997
|
-
"required": true,
|
|
998
|
-
"optionConfig": {
|
|
999
|
-
"optionList": [
|
|
1000
|
-
{ "label": "Male", "code": "M" },
|
|
1001
|
-
{ "label": "Female", "code": "F" }
|
|
1002
|
-
]
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
```
|
|
1006
|
-
|
|
1007
|
-
### 6. Checkbox (CHECKBOX)
|
|
1008
|
-
|
|
1009
|
-
**SubTypes:** `BOOL`, `LIST`
|
|
1010
|
-
|
|
1011
|
-
```json
|
|
1012
|
-
{
|
|
1013
|
-
"name": "agreeToTerms",
|
|
1014
|
-
"label": "I agree to terms",
|
|
1015
|
-
"type": "CHECKBOX",
|
|
1016
|
-
"subType": "BOOL",
|
|
1017
|
-
"required": true
|
|
1018
|
-
}
|
|
1019
|
-
```
|
|
1020
|
-
|
|
1021
|
-
### 7. Chip Selection (CHIP)
|
|
1022
|
-
|
|
1023
|
-
```json
|
|
1024
|
-
{
|
|
1025
|
-
"name": "skills",
|
|
1026
|
-
"label": "Skills",
|
|
1027
|
-
"type": "CHIP",
|
|
1028
|
-
"subType": "MULTIPLE",
|
|
1029
|
-
"optionConfig": {
|
|
1030
|
-
"optionList": [
|
|
1031
|
-
{ "label": "JavaScript", "code": "JS" },
|
|
1032
|
-
{ "label": "Python", "code": "PY" }
|
|
1033
|
-
]
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
```
|
|
1037
|
-
|
|
1038
|
-
### 8. Switch (SWITCH)
|
|
1039
|
-
|
|
1040
|
-
```json
|
|
1041
|
-
{
|
|
1042
|
-
"name": "notifications",
|
|
1043
|
-
"label": "Enable Notifications",
|
|
1044
|
-
"type": "SWITCH",
|
|
1045
|
-
"subType": "BOOL",
|
|
1046
|
-
"defaultValue": true
|
|
1047
|
-
}
|
|
1048
|
-
```
|
|
1049
|
-
|
|
1050
|
-
### 9. Rating (RATING)
|
|
1051
|
-
|
|
1052
|
-
```json
|
|
1053
|
-
{
|
|
1054
|
-
"name": "rating",
|
|
1055
|
-
"label": "Rate your experience",
|
|
1056
|
-
"type": "RATING",
|
|
1057
|
-
"subType": "STAR",
|
|
1058
|
-
"ratingConfig": {
|
|
1059
|
-
"maxRating": 5,
|
|
1060
|
-
"allowHalf": true
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
```
|
|
1064
|
-
|
|
1065
|
-
### 10. File Upload (FILE_UPLOAD)
|
|
1066
|
-
|
|
1067
|
-
Provides a drag-and-drop area with file-type and file-size validation. The field value is an array of `UploadedFile` objects. This field uses a unified `attachmentConfig` for all upload types (PDF, Images, Documents, etc.).
|
|
1068
|
-
|
|
1069
|
-
**SubTypes:** `SINGLE`
|
|
1070
|
-
|
|
1071
|
-
```json
|
|
1072
|
-
{
|
|
1073
|
-
"name": "profilePicture",
|
|
1074
|
-
"label": "Profile Picture",
|
|
1075
|
-
"type": "FILE_UPLOAD",
|
|
1076
|
-
"subType": "SINGLE",
|
|
1077
|
-
"attachmentConfig": {
|
|
1078
|
-
"multiple": false,
|
|
1079
|
-
"maxSizeMB": 2,
|
|
1080
|
-
"accept": "image/*",
|
|
1081
|
-
"acceptLabel": "JPG, PNG, SVG (max 2 MB)"
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
```
|
|
1085
|
-
|
|
1086
|
-
| `attachmentConfig` key | Type | Description |
|
|
1087
|
-
| ---------------------- | ------- | ------------------------------------------------------------ |
|
|
1088
|
-
| `multiple` | boolean | Allow multiple file selection (default: `false`) |
|
|
1089
|
-
| `maxFiles` | number | Max number of files when `multiple: true` (default: `10`) |
|
|
1090
|
-
| `maxSizeMB` | number | Max file size per file in MB (default: `10`) |
|
|
1091
|
-
| `accept` | string | Accepted MIME types / extensions, e.g. `".pdf,.jpg,image/*"` |
|
|
1092
|
-
| `acceptLabel` | string | Human-readable hint shown in the drop zone |
|
|
1093
|
-
|
|
1094
|
-
### 11. Generated Field (GENERATED)
|
|
1095
|
-
|
|
1096
|
-
Auto-calculated fields that update whenever dependent field values change.
|
|
1097
|
-
|
|
1098
|
-
```json
|
|
1099
|
-
{
|
|
1100
|
-
"name": "fullName",
|
|
1101
|
-
"label": "Full Name",
|
|
1102
|
-
"type": "GENERATED",
|
|
1103
|
-
"subType": "FORMULA",
|
|
1104
|
-
"generatedConfig": {
|
|
1105
|
-
"formula": "function fullName(first, last) { return (first || '') + ' ' + (last || ''); }",
|
|
1106
|
-
"variables": ["firstName", "lastName"]
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
```
|
|
1110
|
-
|
|
1111
|
-
### 12. Geography Dynamic (GEOGRAPHY_DYNAMIC)
|
|
1112
|
-
|
|
1113
|
-
Renders a cascading hierarchy of geography dropdowns (e.g. Country → State → District) by fetching a hierarchy template from an MDM API. When the hierarchy has a **branch point** (e.g. State → RURAL | URABN), an **Area Type** selector is automatically inserted so the user can choose which path to follow.
|
|
1114
|
-
|
|
1115
|
-
**Branch point behavior**
|
|
1116
|
-
|
|
1117
|
-
- The Area Type dropdown is generated automatically from the branch children (e.g. "Rural" / "Urban").
|
|
1118
|
-
- Selecting a branch value shows only the dropdowns belonging to that path (District, Taluka, Village …).
|
|
1119
|
-
- Structural branch-type nodes (RURAL, URABN) are **not** rendered as data dropdowns — only their descendants are. The first descendant depends on the last linear parent before the fork (e.g. State), not on the branch node itself.
|
|
1120
|
-
- If the form is pre-populated with an `areaType` value (edit mode), the downstream dropdowns are visible immediately.
|
|
1121
|
-
|
|
1122
|
-
```json
|
|
1123
|
-
{
|
|
1124
|
-
"name": "address",
|
|
1125
|
-
"label": "Address",
|
|
1126
|
-
"type": "GEOGRAPHY_DYNAMIC",
|
|
1127
|
-
"geographyConfig": {
|
|
1128
|
-
"templateApi": {
|
|
1129
|
-
"apiUrl": "https://api.example.com/mdm/hierarchy-templates",
|
|
1130
|
-
"listPath": "templates",
|
|
1131
|
-
"select": "byCode",
|
|
1132
|
-
"selectCode": "ADDRESS_TEMPLATE"
|
|
1133
|
-
},
|
|
1134
|
-
"dataApi": {
|
|
1135
|
-
"apiUrl": "https://api.example.com/mdm/data/{classCode}",
|
|
1136
|
-
"dataPath": "elements",
|
|
1137
|
-
"labelPath": "name[0].text",
|
|
1138
|
-
"valuePath": "code"
|
|
1139
|
-
},
|
|
1140
|
-
"structure": {
|
|
1141
|
-
"rootCodePath": "rootClassCode",
|
|
1142
|
-
"hierarchyListPath": "mdmclasshierarchytemplatestructList",
|
|
1143
|
-
"parentField": "parentCode",
|
|
1144
|
-
"childField": "childCode"
|
|
1145
|
-
},
|
|
1146
|
-
"field": {
|
|
1147
|
-
"colSpan": 4,
|
|
1148
|
-
"payloadPathPrefix": "addressInfo",
|
|
1149
|
-
"branchSubType": "SINGLE",
|
|
1150
|
-
"dependency": {
|
|
1151
|
-
"paramName": "parentDataCode"
|
|
1152
|
-
},
|
|
1153
|
-
"nodes": {
|
|
1154
|
-
"REGION.COUNTRY": { "label": "Country", "required": true },
|
|
1155
|
-
"REGION.STATE": { "label": "State", "payloadField": "stateCode" }
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
```
|
|
1161
|
-
|
|
1162
|
-
**`field` configuration keys**
|
|
1163
|
-
|
|
1164
|
-
| Key | Type | Default | Description |
|
|
1165
|
-
|---|---|---|---|
|
|
1166
|
-
| `colSpan` | `number` | `12` | Column span applied to every generated dropdown |
|
|
1167
|
-
| `payloadPathPrefix` | `string` | `''` | Dot-notation prefix prepended to all generated `payloadPath` values |
|
|
1168
|
-
| `branchSubType` | `'SINGLE' \| 'MULTIPLE'` | `'SINGLE'` | Sub-type for the auto-generated Area Type selector |
|
|
1169
|
-
| `dependency.paramName` | `string` | `'parentDataCode'` | Query parameter name sent when loading child data |
|
|
1170
|
-
| `nodes` | `Record<string, object>` | `{}` | Per-node overrides keyed by class code — supports `label`, `name`, `payloadField`, `required`, `placeholder`, `hidden`, `noDependency` |
|
|
1171
|
-
|
|
1172
|
-
### 13. Rich Text Editor (RICH_TEXT)
|
|
1173
|
-
|
|
1174
|
-
Provides a WYSIWYG rich text editor using Quill.
|
|
1175
|
-
|
|
1176
|
-
```json
|
|
1177
|
-
{
|
|
1178
|
-
"name": "description",
|
|
1179
|
-
"label": "Detailed Description",
|
|
1180
|
-
"type": "RICH_TEXT",
|
|
1181
|
-
"required": true,
|
|
1182
|
-
"richTextConfig": {
|
|
1183
|
-
"placeholder": "Enter detailed information here...",
|
|
1184
|
-
"height": "250px",
|
|
1185
|
-
"headerConfig": ["bold", "italic", "underline", "font", "size", "color", "link", "image", "video"]
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
```
|
|
1189
|
-
|
|
1190
|
-
`headerConfig` controls which toolbar buttons are shown, in order. If omitted, it defaults to:
|
|
1191
|
-
`["bold", "italic", "underline", "font", "size", "color", "link", "image", "video"]`.
|
|
1192
|
-
|
|
1193
|
-
Supported tokens: `bold`, `italic`, `underline`, `strike`, `blockquote`, `code`, `font`, `size`, `color`, `background`, `align`, `header1`, `header2`, `orderedList`, `bulletList`, `subscript`, `superscript`, `indentMinus`, `indentPlus`, `link`, `image`, `video`, `clean`.
|
|
1194
|
-
|
|
1195
|
-
### 14. Link List (LINK_LIST)
|
|
1196
|
-
|
|
1197
|
-
Allows users to manage a list of URLs dynamically. Provides a text input box with a trailing plus icon to add links, displaying them as a list above or below the input field.
|
|
1198
|
-
|
|
1199
|
-
**Configuration Properties (`linkListConfig`):**
|
|
1200
|
-
|
|
1201
|
-
| Property | Type | Description |
|
|
1202
|
-
|---|---|---|
|
|
1203
|
-
| `listPosition` | `'top' \| 'bottom'` | Renders the link list above or below the input field (default: `'bottom'`). |
|
|
1204
|
-
| `editable` | `boolean` | Allows inline editing of existing links in the list (default: `true`). |
|
|
1205
|
-
| `deleteable` | `boolean` | Shows a delete action button on list items (default: `true`). |
|
|
1206
|
-
| `valueFormat` | `'string' \| 'array' \| 'object'` | Format in which data is stored in the form control: `'string'` (separated by `separator`), `'array'` (array of strings), or `'object'` (array of objects) (default: `'string'`). |
|
|
1207
|
-
| `separator` | `string` | Character used to separate links when `valueFormat` is `'string'` (default: `','`). |
|
|
1208
|
-
| `urlKey` | `string` | The object key used to store/load the link URL when `valueFormat` is `'object'` (default: `'linkUrl'`). |
|
|
1209
|
-
| `idField` | `string` | The object key representing the link identifier when resolving API placeholders (default: `'id'`). |
|
|
1210
|
-
| `deleteApiUrl` | `string` | Optional API endpoint to execute an HTTP DELETE call upon removing a saved item (e.g. `/api/v1/links/:id`). |
|
|
1211
|
-
| `editApiUrl` | `string` | Optional API endpoint to execute an HTTP PUT call upon editing a saved item (e.g. `/api/v1/links/:id`). |
|
|
1212
|
-
| `pattern` | `string` | Regular expression pattern for validating each link (default URL validator regex: `^https?://([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,}(/\S*)?$`). |
|
|
1213
|
-
| `patternMessage` | `string` | Custom error message displayed if pattern validation fails. |
|
|
1214
|
-
| `colSpan` | `number` | Grid column span (1-12) for each list item in the 12-column grid. E.g., `6` for two columns side-by-side (default: `12`). |
|
|
1215
|
-
|
|
1216
|
-
**Example Config (Storing as an Array of Objects):**
|
|
1217
|
-
|
|
1218
|
-
```json
|
|
1219
|
-
{
|
|
1220
|
-
"name": "externalLinks",
|
|
1221
|
-
"label": "External Links",
|
|
1222
|
-
"type": "LINK_LIST",
|
|
1223
|
-
"placeholder": "Enter link...",
|
|
1224
|
-
"colSpan": 12,
|
|
1225
|
-
"linkListConfig": {
|
|
1226
|
-
"listPosition": "bottom",
|
|
1227
|
-
"editable": true,
|
|
1228
|
-
"deleteable": true,
|
|
1229
|
-
"valueFormat": "object",
|
|
1230
|
-
"urlKey": "linkUrl"
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
```
|
|
1234
|
-
|
|
1235
|
-
**Example Config (Storing as a Comma-Separated String):**
|
|
1236
|
-
|
|
1237
|
-
```json
|
|
1238
|
-
{
|
|
1239
|
-
"name": "externalLinks",
|
|
1240
|
-
"label": "External Links",
|
|
1241
|
-
"type": "LINK_LIST",
|
|
1242
|
-
"placeholder": "Enter link...",
|
|
1243
|
-
"colSpan": 12,
|
|
1244
|
-
"linkListConfig": {
|
|
1245
|
-
"listPosition": "top",
|
|
1246
|
-
"editable": true,
|
|
1247
|
-
"deleteable": true,
|
|
1248
|
-
"valueFormat": "string",
|
|
1249
|
-
"separator": ","
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
```
|
|
1253
|
-
|
|
1254
|
-
**Example Config (Two-Column Layout with Regex Validation):**
|
|
1255
|
-
|
|
1256
|
-
```json
|
|
1257
|
-
{
|
|
1258
|
-
"name": "externalLinks",
|
|
1259
|
-
"label": "External Links",
|
|
1260
|
-
"type": "LINK_LIST",
|
|
1261
|
-
"placeholder": "Enter link...",
|
|
1262
|
-
"colSpan": 12,
|
|
1263
|
-
"linkListConfig": {
|
|
1264
|
-
"listPosition": "bottom",
|
|
1265
|
-
"editable": true,
|
|
1266
|
-
"deleteable": true,
|
|
1267
|
-
"valueFormat": "object",
|
|
1268
|
-
"urlKey": "linkUrl",
|
|
1269
|
-
"colSpan": 6,
|
|
1270
|
-
"pattern": "^https?://([a-zA-Z0-9\\-]+\\.)+com(/\\S*)?$",
|
|
1271
|
-
"patternMessage": "Only .com domain links are allowed"
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
```
|
|
1275
|
-
|
|
1276
|
-
---
|
|
1277
|
-
|
|
1278
|
-
## Layout System
|
|
1279
|
-
|
|
1280
|
-
### ROW Layout
|
|
1281
|
-
|
|
1282
|
-
Use `type: "ROW"` to place multiple fields horizontally in a 12-column CSS grid. Each child can declare a `colSpan` (1–12). If omitted, the available columns are divided equally among all children.
|
|
1283
|
-
|
|
1284
|
-
```json
|
|
1285
|
-
{
|
|
1286
|
-
"type": "ROW",
|
|
1287
|
-
"subType": "HORIZONTAL",
|
|
1288
|
-
"children": [
|
|
1289
|
-
{
|
|
1290
|
-
"name": "firstName",
|
|
1291
|
-
"label": "First Name",
|
|
1292
|
-
"type": "TEXT_INPUT",
|
|
1293
|
-
"subType": "SHORT",
|
|
1294
|
-
"colSpan": 6
|
|
1295
|
-
},
|
|
1296
|
-
{
|
|
1297
|
-
"name": "lastName",
|
|
1298
|
-
"label": "Last Name",
|
|
1299
|
-
"type": "TEXT_INPUT",
|
|
1300
|
-
"subType": "SHORT",
|
|
1301
|
-
"colSpan": 6
|
|
1302
|
-
}
|
|
1303
|
-
]
|
|
1304
|
-
}
|
|
1305
|
-
```
|
|
1306
|
-
|
|
1307
|
-
### `colSpan` on Section-level Fields
|
|
1308
|
-
|
|
1309
|
-
A non-ROW field that is a direct child of a section can also carry a `colSpan` to participate in the section-level 12-column grid:
|
|
1310
|
-
|
|
1311
|
-
```json
|
|
1312
|
-
{
|
|
1313
|
-
"name": "description",
|
|
1314
|
-
"label": "Description",
|
|
1315
|
-
"type": "TEXT_INPUT",
|
|
1316
|
-
"subType": "LONG",
|
|
1317
|
-
"colSpan": 8
|
|
1318
|
-
}
|
|
1319
|
-
```
|
|
1320
|
-
|
|
1321
|
-
### `colSpan` Reference
|
|
1322
|
-
|
|
1323
|
-
| `colSpan` value | Effective width |
|
|
1324
|
-
| --------------- | --------------- |
|
|
1325
|
-
| 3 | 25% |
|
|
1326
|
-
| 4 | 33% |
|
|
1327
|
-
| 6 | 50% |
|
|
1328
|
-
| 8 | 67% |
|
|
1329
|
-
| 9 | 75% |
|
|
1330
|
-
| 12 (default) | 100% |
|
|
1331
|
-
|
|
1332
|
-
---
|
|
1333
|
-
|
|
1334
|
-
## Configuration Options
|
|
1335
|
-
|
|
1336
|
-
### Form Schema
|
|
1337
|
-
|
|
1338
|
-
```typescript
|
|
1339
|
-
interface FormSchema {
|
|
1340
|
-
entityType: string;
|
|
1341
|
-
label: string; // Can be an i18n key
|
|
1342
|
-
formType: "SECTION" | "STEPPER";
|
|
1343
|
-
showTitle?: boolean;
|
|
1344
|
-
showDescription?: boolean;
|
|
1345
|
-
description?: string; // Can be an i18n key
|
|
1346
|
-
metadata?: { [key: string]: any };
|
|
1347
|
-
sectionConfig?: SectionConfig;
|
|
1348
|
-
stepperConfig?: StepperConfig;
|
|
1349
|
-
submitConfig?: SubmitConfig;
|
|
1350
|
-
editConfig?: EditConfig; // Config for form editing (GET to load, PATCH/PUT to submit)
|
|
1351
|
-
actionBarConfig?: ActionBarConfig; // Flexible action bar (Cancel / Draft / Submit)
|
|
1352
|
-
showActions?: boolean;
|
|
1353
|
-
token?: string; // Full auth token (e.g. "Bearer eyJ…") — applied to all API calls
|
|
1354
|
-
tokenHeader?: string; // HTTP header name (default: "Authorization")
|
|
1355
|
-
}
|
|
1356
|
-
```
|
|
1357
|
-
|
|
1358
|
-
### Action Bar Config
|
|
1359
|
-
|
|
1360
|
-
The `actionBarConfig` defines the buttons rendered at the bottom of the form. It uses a **unified, flexible list of buttons** where each button has its own visual style and behavior-driven action.
|
|
1361
|
-
|
|
1362
|
-
#### Unified Button Model
|
|
1363
|
-
|
|
1364
|
-
In the new model, a button is **only visual** (label, variant, alignment, order). All logic (what happens when clicked) is delegated to an `action` object of type `ActionConfig`.
|
|
1365
|
-
|
|
1366
|
-
| Action `kind` | Behaviour |
|
|
1367
|
-
|---------------|-----------|
|
|
1368
|
-
| `'submit'` | Validates the form and calls the central `submitConfig` or `editConfig` API. |
|
|
1369
|
-
| `'draft'` | Saves the form data without full validation. Merges `extraPayload` if provided. |
|
|
1370
|
-
| `'navigate'` | Simply navigates to the `redirectUrl`. No API call. |
|
|
1371
|
-
| `'api'` | Fires a standalone API call (`apiUrl`, `method`), then optionally navigates. |
|
|
1372
|
-
| `'emit'` | Emits the `actionClick` output event with the button `id` and form data. |
|
|
1373
|
-
| `'next'` | Advances to the next step (Stepper forms only). |
|
|
1374
|
-
| `'prev'` | Goes back to the previous step (Stepper forms only). |
|
|
1375
|
-
|
|
1376
|
-
#### Interfaces
|
|
1377
|
-
|
|
1378
|
-
```typescript
|
|
1379
|
-
interface ActionBarConfig {
|
|
1380
|
-
/** Explicitly ordered list of action buttons. */
|
|
1381
|
-
buttons: ActionButtonConfig[];
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
interface ActionButtonConfig {
|
|
1385
|
-
id: string; // Unique identifier for the button
|
|
1386
|
-
label?: string; // i18n key or plain text
|
|
1387
|
-
variant?: string; // lib-button variant (primary, outline, etc.)
|
|
1388
|
-
alignment?: 'left'|'right'; // Which side of the bar to place the button (default: 'right')
|
|
1389
|
-
order?: number; // Display order within the alignment group
|
|
1390
|
-
hidden?: boolean; // Hide the button
|
|
1391
|
-
disabled?: boolean; // Disable the button
|
|
1392
|
-
showOnLastStepOnly?: boolean; // Section Stepper only: show this button only on the last step
|
|
1393
|
-
action: ActionConfig; // The behavior triggered on click
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
interface ActionConfig {
|
|
1397
|
-
kind: 'submit' | 'draft' | 'navigate' | 'api' | 'emit' | 'next' | 'prev';
|
|
1398
|
-
redirectUrl?: string; // Used by 'navigate', 'api', 'draft', and 'submit'
|
|
1399
|
-
apiUrl?: string; // Required for 'api' kind
|
|
1400
|
-
method?: string; // HTTP method for 'api' actions (default: 'POST')
|
|
1401
|
-
extraPayload?: object; // Static payload for 'api' or 'draft' saves
|
|
1402
|
-
successMessage?: string; // Custom success snackbar message
|
|
1403
|
-
errorMessage?: string; // Custom error snackbar message
|
|
1404
|
-
snackbarConfig?: object; // Snackbar display options
|
|
1405
|
-
}
|
|
1406
|
-
```
|
|
1407
|
-
|
|
1408
|
-
#### Example — Standard Cancel, Draft, and Submit
|
|
1409
|
-
|
|
1410
|
-
This configuration places a "Cancel" button on the left that navigates away, and "Save as Draft" + "Submit" buttons on the right.
|
|
1411
|
-
|
|
1412
|
-
```json
|
|
1413
|
-
{
|
|
1414
|
-
"actionBarConfig": {
|
|
1415
|
-
"buttons": [
|
|
1416
|
-
{
|
|
1417
|
-
"id": "cancel",
|
|
1418
|
-
"label": "Cancel",
|
|
1419
|
-
"variant": "outline",
|
|
1420
|
-
"alignment": "left",
|
|
1421
|
-
"action": { "kind": "navigate", "redirectUrl": "/dashboard" }
|
|
1422
|
-
},
|
|
1423
|
-
{
|
|
1424
|
-
"id": "draft",
|
|
1425
|
-
"label": "Save as Draft",
|
|
1426
|
-
"variant": "secondary",
|
|
1427
|
-
"alignment": "right",
|
|
1428
|
-
"action": { "kind": "draft", "extraPayload": { "status": "DRAFT" } }
|
|
1429
|
-
},
|
|
1430
|
-
{
|
|
1431
|
-
"id": "submit",
|
|
1432
|
-
"label": "Submit",
|
|
1433
|
-
"variant": "primary",
|
|
1434
|
-
"alignment": "right",
|
|
1435
|
-
"action": { "kind": "submit" }
|
|
1436
|
-
}
|
|
1437
|
-
]
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
```
|
|
1441
|
-
|
|
1442
|
-
#### Example — Stepper with Back/Next and a Custom Finish Button
|
|
1443
|
-
|
|
1444
|
-
```json
|
|
1445
|
-
{
|
|
1446
|
-
"actionBarConfig": {
|
|
1447
|
-
"buttons": [
|
|
1448
|
-
{
|
|
1449
|
-
"id": "back",
|
|
1450
|
-
"label": "Back",
|
|
1451
|
-
"alignment": "left",
|
|
1452
|
-
"action": { "kind": "prev" }
|
|
1453
|
-
},
|
|
1454
|
-
{
|
|
1455
|
-
"id": "save-and-exit",
|
|
1456
|
-
"label": "Save & Exit",
|
|
1457
|
-
"alignment": "right",
|
|
1458
|
-
"variant": "outline",
|
|
1459
|
-
"action": { "kind": "api", "apiUrl": "/api/v1/sessions/save-progress" }
|
|
1460
|
-
},
|
|
1461
|
-
{
|
|
1462
|
-
"id": "next",
|
|
1463
|
-
"label": "Next Step",
|
|
1464
|
-
"alignment": "right",
|
|
1465
|
-
"variant": "primary",
|
|
1466
|
-
"action": { "kind": "submit" }
|
|
1467
|
-
}
|
|
1468
|
-
]
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
```
|
|
1472
|
-
|
|
1473
|
-
#### Notes
|
|
1474
|
-
|
|
1475
|
-
- **Alignment**: Buttons with `alignment: 'left'` are grouped on the left side. `alignment: 'right'` (the default) are on the right. Both groups use `flexbox` for layout, pushing them to opposite edges of the form.
|
|
1476
|
-
- **Ordering**: Within each side, buttons are sorted by the `order` property (numeric, ascending). If `order` is omitted, they appear in the order defined in the JSON array.
|
|
1477
|
-
- **Custom Actions**: Use `kind: 'emit'` to handle logic in your parent component. Whenever clicked, the `<lib-smart-form>` will emit an `(actionClick)` event containing the button's `id` and the current form data.
|
|
1478
|
-
- **Draft Behavior**: Just like the legacy mode, `'kind': 'draft'` skips required field validation and collects whatever data is currently in the form.
|
|
1479
|
-
- **`showOnLastStepOnly`**: When `true` and the form is in **Section Stepper** mode, the button is hidden on all intermediate steps and only appears on the last step. Has no effect in regular (non-stepper) layouts — the button always shows there. Ideal for Submit buttons that should not be reachable until the user has navigated through all steps.
|
|
1480
|
-
|
|
1481
|
-
### Section Config
|
|
1482
|
-
|
|
1483
|
-
```typescript
|
|
1484
|
-
interface SectionConfig {
|
|
1485
|
-
children: FieldConfig[];
|
|
1486
|
-
allowMulti?: boolean; // Enables repeater mode — see Repeatable Sections
|
|
1487
|
-
name?: string; // Required when allowMulti is true (used as FormArray key)
|
|
1488
|
-
label?: string; // Can be an i18n key
|
|
1489
|
-
isEnabled?: boolean; // Static flag to explicitly hide the entire section if false
|
|
1490
|
-
}
|
|
1491
|
-
```
|
|
1492
|
-
|
|
1493
|
-
### Submit & Edit Configs
|
|
1494
|
-
|
|
1495
|
-
```typescript
|
|
1496
|
-
interface SubmitConfig {
|
|
1497
|
-
apiUrl: string;
|
|
1498
|
-
method?: "POST" | "PUT" | "PATCH";
|
|
1499
|
-
successMessage?: string; // Can be an i18n key
|
|
1500
|
-
errorMessage?: string; // Can be an i18n key
|
|
1501
|
-
redirectUrl?: string; // Redirect after success
|
|
1502
|
-
extraPayload?: { [key: string]: any }; // Static extra fields for payload
|
|
1503
|
-
snackbarConfig?: SnackbarConfigOverride; // Customizes the API feedback alerts
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
interface EditConfig {
|
|
1507
|
-
loadApiUrl: string; // Custom GET API to load existing form data
|
|
1508
|
-
submitApiUrl: string; // PATCH/PUT/POST API for updating
|
|
1509
|
-
submitMethod?: "PATCH" | "PUT" | "POST";
|
|
1510
|
-
successMessage?: string;
|
|
1511
|
-
errorMessage?: string;
|
|
1512
|
-
redirectUrl?: string;
|
|
1513
|
-
extraPayload?: { [key: string]: any };
|
|
1514
|
-
snackbarConfig?: SnackbarConfigOverride;
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
interface SnackbarConfigOverride {
|
|
1518
|
-
duration?: number; // Auto-dismiss time (default 5000)
|
|
1519
|
-
horizontalPosition?: "start" | "center" | "end" | "left" | "right";
|
|
1520
|
-
verticalPosition?: "top" | "bottom";
|
|
1521
|
-
showCloseButton?: boolean;
|
|
1522
|
-
}
|
|
1523
|
-
```
|
|
1524
|
-
|
|
1525
|
-
### Field Config (key properties)
|
|
1526
|
-
|
|
1527
|
-
```typescript
|
|
1528
|
-
interface FieldConfig {
|
|
1529
|
-
name?: string;
|
|
1530
|
-
label?: string; // Can be an i18n key
|
|
1531
|
-
type: string;
|
|
1532
|
-
subType: string;
|
|
1533
|
-
required?: boolean;
|
|
1534
|
-
disabled?: boolean;
|
|
1535
|
-
defaultValue?: any;
|
|
1536
|
-
placeholder?: string; // Can be an i18n key
|
|
1537
|
-
hint?: string; // Can be an i18n key
|
|
1538
|
-
colSpan?: number; // Column span in 12-column grid (1–12)
|
|
1539
|
-
payloadPath?: string; // Dot-notation path for nested payload mapping (e.g., 'status.code')
|
|
1540
|
-
visibilityExpression?: string;
|
|
1541
|
-
isEnabled?: boolean; // Static flag to explicitly disable/hide the field if false
|
|
1542
|
-
readonly?: boolean; // Whether the field is read-only (shows lock icon)
|
|
1543
|
-
suffixActionIcons?: SuffixActionIcon[]; // Clickable action icons inside the input
|
|
1544
|
-
sectionConfig?: SectionConfig;
|
|
1545
|
-
textConfig?: TextConfig;
|
|
1546
|
-
numberConfig?: NumberConfig;
|
|
1547
|
-
dateConfig?: DateConfig;
|
|
1548
|
-
timeConfig?: TimeConfig;
|
|
1549
|
-
optionConfig?: OptionConfig;
|
|
1550
|
-
attachmentConfig?: AttachmentConfig;
|
|
1551
|
-
ratingConfig?: RatingConfig;
|
|
1552
|
-
generatedConfig?: GeneratedConfig;
|
|
1553
|
-
richTextConfig?: RichTextConfig;
|
|
1554
|
-
children?: FieldConfig[]; // For ROW and GROUP fields
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
interface SuffixActionIcon {
|
|
1558
|
-
icon: string; // Material icon name (e.g. 'edit', 'refresh')
|
|
1559
|
-
actionId: string; // Unique ID emitted on click
|
|
1560
|
-
tooltip?: string; // Optional tooltip shown on hover
|
|
1561
|
-
color?: string; // Optional custom color override (e.g. '#16A34A')
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
interface AttachmentConfig {
|
|
1565
|
-
multiple?: boolean;
|
|
1566
|
-
maxFiles?: number;
|
|
1567
|
-
maxSizeMB?: number;
|
|
1568
|
-
accept?: string;
|
|
1569
|
-
acceptLabel?: string;
|
|
1570
|
-
allowedExtensions?: string[]; // Legacy
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
interface RichTextConfig {
|
|
1574
|
-
height?: string; // CSS height for the editor (e.g. "200px")
|
|
1575
|
-
placeholder?: string; // Custom placeholder text
|
|
1576
|
-
maxLength?: number;
|
|
1577
|
-
showCharCount?: boolean;
|
|
1578
|
-
headerConfig?: string[]; // Toolbar tokens to show, in order (default: bold/italic/underline/font/size/color/link/image/video)
|
|
1579
|
-
}
|
|
1580
|
-
```
|
|
1581
|
-
|
|
1582
|
-
---
|
|
1583
|
-
|
|
1584
|
-
## Advanced Features
|
|
1585
|
-
|
|
1586
|
-
### 1. Conditional Visibility
|
|
1587
|
-
|
|
1588
|
-
Show/hide fields based on other field values using a JavaScript-like expression:
|
|
1589
|
-
|
|
1590
|
-
```json
|
|
1591
|
-
{
|
|
1592
|
-
"name": "otherReason",
|
|
1593
|
-
"label": "Please specify",
|
|
1594
|
-
"type": "TEXT_INPUT",
|
|
1595
|
-
"subType": "SHORT",
|
|
1596
|
-
"visibilityExpression": "reason === 'OTHER'",
|
|
1597
|
-
"required": true
|
|
1598
|
-
}
|
|
1599
|
-
```
|
|
1600
|
-
|
|
1601
|
-
The expression is evaluated against the live form data. When a field is hidden its control is disabled so its value is excluded from submission.
|
|
1602
|
-
|
|
1603
|
-
### 2. Auto-Calculated Fields
|
|
1604
|
-
|
|
1605
|
-
Fields that automatically update based on other fields:
|
|
1606
|
-
|
|
1607
|
-
```json
|
|
1608
|
-
{
|
|
1609
|
-
"name": "total",
|
|
1610
|
-
"label": "Total Amount",
|
|
1611
|
-
"type": "GENERATED",
|
|
1612
|
-
"subType": "FORMULA",
|
|
1613
|
-
"generatedConfig": {
|
|
1614
|
-
"formula": "function total(price, quantity) { return (price || 0) * (quantity || 0); }",
|
|
1615
|
-
"variables": ["price", "quantity"]
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
```
|
|
1619
|
-
|
|
1620
|
-
### 3. Repeatable Sections (`allowMulti` on GROUP)
|
|
1621
|
-
|
|
1622
|
-
Allow users to add multiple instances of a section (e.g., multiple work experiences):
|
|
1623
|
-
|
|
1624
|
-
```json
|
|
1625
|
-
{
|
|
1626
|
-
"type": "GROUP",
|
|
1627
|
-
"subType": "SECTION",
|
|
1628
|
-
"sectionConfig": {
|
|
1629
|
-
"label": "Work Experience",
|
|
1630
|
-
"allowMulti": true,
|
|
1631
|
-
"name": "experienceList",
|
|
1632
|
-
"children": [
|
|
1633
|
-
{
|
|
1634
|
-
"name": "company",
|
|
1635
|
-
"label": "Company",
|
|
1636
|
-
"type": "TEXT_INPUT",
|
|
1637
|
-
"subType": "SHORT",
|
|
1638
|
-
"required": true
|
|
1639
|
-
},
|
|
1640
|
-
{
|
|
1641
|
-
"name": "position",
|
|
1642
|
-
"label": "Position",
|
|
1643
|
-
"type": "TEXT_INPUT",
|
|
1644
|
-
"subType": "SHORT",
|
|
1645
|
-
"required": true
|
|
1646
|
-
}
|
|
1647
|
-
]
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
```
|
|
1651
|
-
|
|
1652
|
-
- `name` is **required** when `allowMulti: true` — it becomes the `FormArray` key on the root `FormGroup`.
|
|
1653
|
-
- Each instance is a completely isolated `FormGroup`; data does not leak between instances.
|
|
1654
|
-
- The submitted value for this key is an **array of objects**, e.g. `{ "experienceList": [{ "company": "...", "position": "..." }, ...] }`.
|
|
1655
|
-
- **Add is gated on completeness.** Clicking "Add" is refused if the *previous* instance still has an invalid required field — that instance is re-expanded and marked touched (its errors render) instead of a new blank row being added. This prevents users from stacking up several incomplete rows with no indication anything is wrong until final submit.
|
|
1656
|
-
- **Sibling dropdowns auto-exclude taken values.** A single-select `DROPDOWN` field inside a repeater automatically hides values already chosen by *other* instances of the same repeater — e.g. picking "English" in Language Details #1 removes it from Language Details #2's option list. This is automatic for any single-select `DROPDOWN` in a repeater; no schema config is needed. It does **not** apply to `MULTIPLE`-subtype dropdowns.
|
|
1657
|
-
|
|
1658
|
-
### 4. Cross-Field Match Validation (e.g. confirm password)
|
|
1659
|
-
|
|
1660
|
-
Use `textConfig.matchField` on the confirmation field to enforce equality:
|
|
1661
|
-
|
|
1662
|
-
```json
|
|
1663
|
-
{
|
|
1664
|
-
"name": "confirmPassword",
|
|
1665
|
-
"label": "Confirm Password",
|
|
1666
|
-
"type": "TEXT_INPUT",
|
|
1667
|
-
"subType": "PASSWORD",
|
|
1668
|
-
"required": true,
|
|
1669
|
-
"textConfig": { "matchField": "password" }
|
|
1670
|
-
}
|
|
1671
|
-
```
|
|
1672
|
-
|
|
1673
|
-
- Validation fires bi-directionally: if the user edits the source field after confirming, the error clears automatically.
|
|
1674
|
-
- Error key: `passwordMismatch`, shown as "Passwords do not match".
|
|
1675
|
-
|
|
1676
|
-
### 5. Row Layouts with Column Spans
|
|
1677
|
-
|
|
1678
|
-
Display multiple fields in a row with fine-grained width control:
|
|
1679
|
-
|
|
1680
|
-
```json
|
|
1681
|
-
{
|
|
1682
|
-
"type": "ROW",
|
|
1683
|
-
"subType": "HORIZONTAL",
|
|
1684
|
-
"children": [
|
|
1685
|
-
{
|
|
1686
|
-
"name": "shortCode",
|
|
1687
|
-
"label": "Short Code",
|
|
1688
|
-
"type": "TEXT_INPUT",
|
|
1689
|
-
"subType": "SHORT",
|
|
1690
|
-
"colSpan": 3
|
|
1691
|
-
},
|
|
1692
|
-
{
|
|
1693
|
-
"name": "donorType",
|
|
1694
|
-
"label": "Donor Type",
|
|
1695
|
-
"type": "DROPDOWN",
|
|
1696
|
-
"subType": "SINGLE",
|
|
1697
|
-
"colSpan": 3
|
|
1698
|
-
},
|
|
1699
|
-
{
|
|
1700
|
-
"name": "regNumber",
|
|
1701
|
-
"label": "Reg. Number",
|
|
1702
|
-
"type": "TEXT_INPUT",
|
|
1703
|
-
"subType": "SHORT",
|
|
1704
|
-
"colSpan": 3
|
|
1705
|
-
},
|
|
1706
|
-
{
|
|
1707
|
-
"name": "country",
|
|
1708
|
-
"label": "Country",
|
|
1709
|
-
"type": "DROPDOWN",
|
|
1710
|
-
"subType": "SINGLE",
|
|
1711
|
-
"colSpan": 3
|
|
1712
|
-
}
|
|
1713
|
-
]
|
|
1714
|
-
}
|
|
1715
|
-
```
|
|
1716
|
-
|
|
1717
|
-
### 6. Suffix Action Icons
|
|
1718
|
-
|
|
1719
|
-
Add clickable action icons inside `TEXT_INPUT` (short text, email, phone) and `NUMBER_INPUT` fields to trigger custom logic in the host application (e.g. password visibility toggling, code validation, API lookup).
|
|
1720
|
-
|
|
1721
|
-
```json
|
|
1722
|
-
{
|
|
1723
|
-
"name": "promoCode",
|
|
1724
|
-
"label": "PROMO_CODE.LABEL",
|
|
1725
|
-
"type": "TEXT_INPUT",
|
|
1726
|
-
"subType": "SHORT",
|
|
1727
|
-
"suffixActionIcons": [
|
|
1728
|
-
{
|
|
1729
|
-
"icon": "refresh",
|
|
1730
|
-
"actionId": "regenerate_code",
|
|
1731
|
-
"tooltip": "Regenerate Promo Code",
|
|
1732
|
-
"color": "#1E3A8A"
|
|
1733
|
-
},
|
|
1734
|
-
{
|
|
1735
|
-
"icon": "check_circle",
|
|
1736
|
-
"actionId": "verify_code",
|
|
1737
|
-
"tooltip": "Verify Validity",
|
|
1738
|
-
"color": "#16A34A"
|
|
1739
|
-
}
|
|
1740
|
-
]
|
|
1741
|
-
}
|
|
1742
|
-
```
|
|
1743
|
-
|
|
1744
|
-
When clicked, the `(suffixActionClick)` output event is emitted. The host application catches this event to run specific actions (such as generating a new promo code or verifying its validity).
|
|
1745
|
-
|
|
1746
|
-
> [!NOTE]
|
|
1747
|
-
> Suffix action icons are ignored if the field is set to `readonly: true`. In that state, the default security lock icon (`lock`) takes visual precedence.
|
|
1748
|
-
|
|
1749
|
-
---
|
|
1750
|
-
|
|
1751
|
-
## i18n / Translation Support
|
|
1752
|
-
|
|
1753
|
-
The `SmartFormComponent` accepts a `[labels]` input that is a **flat key–value map** of i18n strings (or an object with a `labelsObject` property following the same pattern used by `ConfigurableFormComponent`).
|
|
1754
|
-
|
|
1755
|
-
When `labels` is provided, the component recursively walks the parsed schema **before rendering** and replaces every matching key with its translated value.
|
|
1756
|
-
|
|
1757
|
-
### Action Labels
|
|
1758
|
-
|
|
1759
|
-
The form action buttons (Next, Submit, Previous, Add, Remove) and file upload error messages can be localized by providing keys in the `labels` property of the `FormSchema`.
|
|
1760
|
-
|
|
1761
|
-
```json
|
|
1762
|
-
{
|
|
1763
|
-
"entityType": "USER",
|
|
1764
|
-
"label": "FORM.TITLE",
|
|
1765
|
-
"formType": "SECTION",
|
|
1766
|
-
"labels": {
|
|
1767
|
-
"nextLabel": "APP.BUTTON.NEXT",
|
|
1768
|
-
"submitLabel": "APP.BUTTON.SUBMIT",
|
|
1769
|
-
"previousLabel": "APP.BUTTON.PREVIOUS",
|
|
1770
|
-
"addLabel": "APP.BUTTON.ADD",
|
|
1771
|
-
"removeLabel": "APP.BUTTON.REMOVE",
|
|
1772
|
-
"fileTypeError": "\"{fileName}\" is not a supported file type.",
|
|
1773
|
-
"fileSizeError": "\"{fileName}\" exceeds the maximum allowed size of {maxSizeMB} MB.",
|
|
1774
|
-
"maxFilesError": "Maximum {maxFiles} files allowed.",
|
|
1775
|
-
"fileUploadFailed": "Failed to upload \"{fileName}\". Please try again.",
|
|
1776
|
-
"fileDeleteFailed": "Failed to delete \"{fileName}\". Please try again."
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
```
|
|
1780
|
-
|
|
1781
|
-
If a mapping is not provided, the buttons and messages fall back to default English strings.
|
|
1782
|
-
|
|
1783
|
-
**File upload label placeholders**
|
|
1784
|
-
|
|
1785
|
-
| Label key | Dynamic placeholders | Default English text |
|
|
1786
|
-
|---|---|---|
|
|
1787
|
-
| `fileTypeError` | `{fileName}` | `"{fileName}" is not a supported file type.` |
|
|
1788
|
-
| `fileSizeError` | `{fileName}`, `{maxSizeMB}` | `"{fileName}" exceeds the maximum allowed size of {maxSizeMB} MB.` |
|
|
1789
|
-
| `maxFilesError` | `{maxFiles}` | `Maximum {maxFiles} files allowed.` |
|
|
1790
|
-
| `fileUploadFailed` | `{fileName}` | `Failed to upload "{fileName}". Please try again.` |
|
|
1791
|
-
| `fileDeleteFailed` | `{fileName}` | `Failed to delete "{fileName}". Please try again.` |
|
|
1792
|
-
|
|
1793
|
-
### Translatable Schema Properties
|
|
1794
|
-
|
|
1795
|
-
| Property path | Description |
|
|
1796
|
-
| --------------------------------------- | ---------------------------------------------- |
|
|
1797
|
-
| `schema.label` | Form title |
|
|
1798
|
-
| `schema.description` | Form description |
|
|
1799
|
-
| `schema.submitConfig.successMessage` | Success alert text |
|
|
1800
|
-
| `schema.submitConfig.errorMessage` | Error alert text |
|
|
1801
|
-
| `sectionConfig.label` | Section heading |
|
|
1802
|
-
| `field.label` | Field label |
|
|
1803
|
-
| `field.placeholder` | Field placeholder |
|
|
1804
|
-
| `field.hint` | Field hint text |
|
|
1805
|
-
| `field.textConfig.patternMessage` | Custom pattern error message |
|
|
1806
|
-
| `field.attachmentConfig.acceptLabel` | File drop-zone hint (for all attachment types) |
|
|
1807
|
-
| `field.optionConfig.optionList[].label` | Static option labels |
|
|
1808
|
-
| `schema.labels.nextLabel` | Key for "Next" button text (Stepper) |
|
|
1809
|
-
| `schema.labels.submitLabel` | Key for "Submit" button text |
|
|
1810
|
-
| `schema.labels.previousLabel` | Key for "Previous" button text (Stepper) |
|
|
1811
|
-
| `schema.labels.addLabel` | Key for "Add" button text (Repeater) |
|
|
1812
|
-
| `schema.labels.removeLabel` | Key for "Remove" button text (Repeater) |
|
|
1813
|
-
| `schema.labels.fileTypeError` | File upload: unsupported file type error |
|
|
1814
|
-
| `schema.labels.fileSizeError` | File upload: file too large error |
|
|
1815
|
-
| `schema.labels.maxFilesError` | File upload: too many files error |
|
|
1816
|
-
| `schema.labels.fileUploadFailed` | File upload: API upload failure message |
|
|
1817
|
-
| `schema.labels.fileDeleteFailed` | File upload: API delete failure message |
|
|
1818
|
-
|
|
1819
|
-
> **Note:** Translation logic is centralized in the `SmartFormTranslationUtils` class. If you add new translatable properties to the `FormSchema` or `FieldConfig` models, ensure they are also registered in the `SmartFormTranslationUtils.translateSchema` method to be correctly processed.
|
|
1820
|
-
|
|
1821
|
-
Translation is applied recursively — it covers fields inside `ROW` children, `GROUP` children, nested `sectionConfig` sections, and all stepper steps.
|
|
1822
|
-
|
|
1823
|
-
### Usage
|
|
1824
|
-
|
|
1825
|
-
```html
|
|
1826
|
-
<lib-smart-form
|
|
1827
|
-
[formJson]="formJson"
|
|
1828
|
-
[labels]="labels"
|
|
1829
|
-
(submit)="onSubmit($event)"
|
|
1830
|
-
>
|
|
1831
|
-
</lib-smart-form>
|
|
1832
|
-
```
|
|
1833
|
-
|
|
1834
|
-
```typescript
|
|
1835
|
-
// Flat map — keys exactly match the strings used in the JSON schema
|
|
1836
|
-
labels = {
|
|
1837
|
-
"SMART_FORM.TITLE": "User Registration",
|
|
1838
|
-
"SMART_FORM.FIELD.FIRST_NAME": "First Name",
|
|
1839
|
-
"SMART_FORM.PH.FIRST_NAME": "Enter your first name",
|
|
1840
|
-
};
|
|
1841
|
-
```
|
|
1842
|
-
|
|
1843
|
-
When `labels` changes (e.g., user switches language), the form is automatically re-parsed and all labels are re-translated.
|
|
1844
|
-
|
|
1845
|
-
### Compatibility with `labelsObject`
|
|
1846
|
-
|
|
1847
|
-
The component also accepts the pattern used by `ConfigurableFormComponent`:
|
|
1848
|
-
|
|
1849
|
-
```typescript
|
|
1850
|
-
labels = {
|
|
1851
|
-
errorMaxItemsAllowed: "...",
|
|
1852
|
-
labelsObject: { "SMART_FORM.TITLE": "User Registration" /* ... */ },
|
|
1853
|
-
};
|
|
1854
|
-
```
|
|
1855
|
-
|
|
1856
|
-
If `labels.labelsObject` is present, translations are read from there; otherwise the top-level object is used directly.
|
|
1857
|
-
|
|
1858
|
-
---
|
|
1859
|
-
|
|
1860
|
-
## API Submission
|
|
1861
|
-
|
|
1862
|
-
### Automatic API Submission (CREATE / POST)
|
|
1863
|
-
|
|
1864
|
-
Configure the form to automatically submit to an API endpoint by providing a `submitConfig`. You can also provide static additional payload fields that the form will merge into the final API request.
|
|
1865
|
-
|
|
1866
|
-
```json
|
|
1867
|
-
{
|
|
1868
|
-
"entityType": "USER",
|
|
1869
|
-
"submitConfig": {
|
|
1870
|
-
"apiUrl": "https://api.example.com/users",
|
|
1871
|
-
"method": "POST",
|
|
1872
|
-
"successMessage": "User created successfully!",
|
|
1873
|
-
"redirectUrl": "https://platformcommons.dev/home",
|
|
1874
|
-
"extraPayload": {
|
|
1875
|
-
"channelCode": "CHANNEL.COMMONS.GRE_DEFAULT_CHANNEL",
|
|
1876
|
-
"marketCode": "COMMONS.GRE",
|
|
1877
|
-
"status": {
|
|
1878
|
-
"code": "REQUEST_STATUS.SUBMITTED"
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
1883
|
-
```
|
|
1884
|
-
|
|
1885
|
-
### Form Editing (EDIT / PATCH)
|
|
1886
|
-
|
|
1887
|
-
To support editing an existing record, switch the SmartFormComponent mode to `EDIT` and provide an `editConfig` in the `FormSchema`. The component will fetch the existing data using `loadApiUrl` before the form renders, and submit changes to `submitApiUrl` using the defined `submitMethod` (default `PATCH`).
|
|
1888
|
-
|
|
1889
|
-
```html
|
|
1890
|
-
<lib-smart-form [formJson]="formJson" mode="EDIT" (submit)="onSubmit($event)">
|
|
1891
|
-
</lib-smart-form>
|
|
1892
|
-
```
|
|
1893
|
-
|
|
1894
|
-
```json
|
|
1895
|
-
{
|
|
1896
|
-
"entityType": "USER",
|
|
1897
|
-
"editConfig": {
|
|
1898
|
-
"loadApiUrl": "https://api.example.com/users/123",
|
|
1899
|
-
"submitApiUrl": "https://api.example.com/users/123",
|
|
1900
|
-
"submitMethod": "PATCH",
|
|
1901
|
-
"successMessage": "User updated successfully!",
|
|
1902
|
-
"redirectUrl": "https://platformcommons.dev/home",
|
|
1903
|
-
"extraPayload": {
|
|
1904
|
-
"updatedBy": "admin"
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
}
|
|
1908
|
-
```
|
|
1909
|
-
|
|
1910
|
-
### Nested Payload Mapping
|
|
1911
|
-
|
|
1912
|
-
By default, the form submits a flat object matching the field names. If your API requires nested structures (like `status.code` or `address.city`), specify the nested hierarchy using the `payloadPath` property on the `FieldConfig`.
|
|
1913
|
-
|
|
1914
|
-
```json
|
|
1915
|
-
{
|
|
1916
|
-
"name": "statusCodeInput",
|
|
1917
|
-
"label": "Status",
|
|
1918
|
-
"type": "TEXT_INPUT",
|
|
1919
|
-
"subType": "SHORT",
|
|
1920
|
-
"payloadPath": "status.code"
|
|
1921
|
-
}
|
|
1922
|
-
```
|
|
1923
|
-
|
|
1924
|
-
Upon submission, the value from `statusCodeInput` will be formatted as:
|
|
1925
|
-
|
|
1926
|
-
```json
|
|
1927
|
-
{
|
|
1928
|
-
"status": {
|
|
1929
|
-
"code": "Submitted value"
|
|
1930
|
-
}
|
|
1931
|
-
}
|
|
1932
|
-
```
|
|
1933
|
-
|
|
1934
|
-
**Array Support in Payload Mapping:**
|
|
1935
|
-
You can also specify array indices directly in the `payloadPath` if your API expects values inside nested arrays.
|
|
1936
|
-
|
|
1937
|
-
```json
|
|
1938
|
-
{
|
|
1939
|
-
"name": "firstName",
|
|
1940
|
-
"label": "First Name",
|
|
1941
|
-
"type": "TEXT_INPUT",
|
|
1942
|
-
"subType": "SHORT",
|
|
1943
|
-
"payloadPath": "name[0].labels.text"
|
|
1944
|
-
}
|
|
1945
|
-
```
|
|
1946
|
-
|
|
1947
|
-
Upon submission, the value from `firstName` will be formatted as:
|
|
1948
|
-
|
|
1949
|
-
```json
|
|
1950
|
-
{
|
|
1951
|
-
"name": [
|
|
1952
|
-
{
|
|
1953
|
-
"labels": {
|
|
1954
|
-
"text": "Submitted value"
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
|
-
]
|
|
1958
|
-
}
|
|
1959
|
-
```
|
|
1960
|
-
|
|
1961
|
-
### Deep-Merging with `extraPayload`
|
|
1962
|
-
|
|
1963
|
-
The `SmartFormComponent` intelligently deep-merges anything you provide in `submitConfig.extraPayload` (or `editConfig.extraPayload`) with the payload constructed from the form fields.
|
|
1964
|
-
|
|
1965
|
-
This means you can provide static extra properties that share the same nested structure (even arrays), and they will perfectly overlay each other.
|
|
1966
|
-
|
|
1967
|
-
**Example: Merging Array Values**
|
|
1968
|
-
Using the `firstName` setup above, if you needed your API payload to look like this:
|
|
1969
|
-
|
|
1970
|
-
```json
|
|
1971
|
-
{
|
|
1972
|
-
"name": [
|
|
1973
|
-
{
|
|
1974
|
-
"id": 0,
|
|
1975
|
-
"labels": {
|
|
1976
|
-
"text": "John"
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
]
|
|
1980
|
-
}
|
|
1981
|
-
```
|
|
1982
|
-
|
|
1983
|
-
You simply put the static `"id": 0` inside your `extraPayload` mirroring the structure:
|
|
1984
|
-
|
|
1985
|
-
```json
|
|
1986
|
-
{
|
|
1987
|
-
"entityType": "USER",
|
|
1988
|
-
"submitConfig": {
|
|
1989
|
-
"apiUrl": "https://api.example.com/users",
|
|
1990
|
-
"extraPayload": {
|
|
1991
|
-
"name": [
|
|
1992
|
-
{
|
|
1993
|
-
"id": 0
|
|
1994
|
-
}
|
|
1995
|
-
]
|
|
1996
|
-
}
|
|
1997
|
-
},
|
|
1998
|
-
"sectionConfig": {
|
|
1999
|
-
"children": [
|
|
2000
|
-
{
|
|
2001
|
-
"name": "firstName",
|
|
2002
|
-
"payloadPath": "name[0].labels.text",
|
|
2003
|
-
"type": "TEXT_INPUT"
|
|
2004
|
-
}
|
|
2005
|
-
]
|
|
2006
|
-
}
|
|
2007
|
-
}
|
|
2008
|
-
```
|
|
2009
|
-
|
|
2010
|
-
The internal deep-merge algorithm combines the values at `name[0]` to produce the exact desired payload.
|
|
2011
|
-
|
|
2012
|
-
"label": "User Registration",
|
|
2013
|
-
"formType": "SECTION",
|
|
2014
|
-
"submitConfig": {
|
|
2015
|
-
"apiUrl": "https://api.example.com/users",
|
|
2016
|
-
"method": "POST",
|
|
2017
|
-
"successMessage": "User created successfully",
|
|
2018
|
-
"errorMessage": "Failed to create user"
|
|
2019
|
-
},
|
|
2020
|
-
"sectionConfig": { "children": [...] }
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
````
|
|
2024
|
-
|
|
2025
|
-
### Manual Submission
|
|
2026
|
-
|
|
2027
|
-
If no `submitConfig` is provided, handle submission in the parent component via the `(submit)` output:
|
|
2028
|
-
|
|
2029
|
-
```typescript
|
|
2030
|
-
onSubmit(data: any) {
|
|
2031
|
-
this.http.post("https://api.example.com/users", data).subscribe(
|
|
2032
|
-
response => console.log("Success", response),
|
|
2033
|
-
error => console.error("Error", error)
|
|
2034
|
-
);
|
|
2035
|
-
}
|
|
2036
|
-
````
|
|
2037
|
-
|
|
2038
|
-
### Submitted Data Shape
|
|
2039
|
-
|
|
2040
|
-
- **Flat fields** → top-level key/value pairs. If `payloadPath` is set, maps the value to the specified nested path.
|
|
2041
|
-
- **GROUP (Visual Section)** → If a group lacks an explicit `name` or `allowMulti`, it is purely visual. Its contents are **flattened** directly onto the target payload layer.
|
|
2042
|
-
- **GROUP (Structural Block)** → If a group defines `sectionConfig.name`, `field.name`, or `allowMulti: true`.
|
|
2043
|
-
- Its payload is built as a nested object (or an array of objects if `allowMulti`).
|
|
2044
|
-
- The object key is determined by priority: `sectionConfig.name` → `field.name` → a generated camelCase derived from `sectionConfig.label` → `__group__`.
|
|
2045
|
-
- If a `payloadPath` is provided on the structural block, the payload is mapped to that precise dot-notation path instead of the generated default key.
|
|
2046
|
-
|
|
2047
|
-
```json
|
|
2048
|
-
{
|
|
2049
|
-
// Flattened visual group fields (no 'name' specified on the group layout)
|
|
2050
|
-
"someOtherField": "Flattened value",
|
|
2051
|
-
|
|
2052
|
-
// Structural single block nested under derived key
|
|
2053
|
-
"basicDetails": {
|
|
2054
|
-
"firstName": "John",
|
|
2055
|
-
"lastName": "Doe"
|
|
2056
|
-
},
|
|
2057
|
-
|
|
2058
|
-
// Structural Repeater block (allowMulti: true)
|
|
2059
|
-
"experienceList": [
|
|
2060
|
-
{ "company": "Acme", "position": "Engineer" },
|
|
2061
|
-
{ "company": "Globex", "position": "Lead" }
|
|
2062
|
-
]
|
|
2063
|
-
}
|
|
2064
|
-
```
|
|
2065
|
-
|
|
2066
|
-
---
|
|
2067
|
-
|
|
2068
|
-
## API Authentication (Token)
|
|
2069
|
-
|
|
2070
|
-
The Smart Form can attach an auth token to **every library-initiated HTTP call** — this includes:
|
|
2071
|
-
|
|
2072
|
-
- Dropdown / Radio / Chip option loading (`optionConfig.apiUrl`, `apiUrls`, `optionUrl`)
|
|
2073
|
-
- Dependent dropdown refreshes (cascading selects)
|
|
2074
|
-
- Automatic form submission (`submitConfig.apiUrl`)
|
|
2075
|
-
|
|
2076
|
-
### How It Works
|
|
2077
|
-
|
|
2078
|
-
The `token` and `tokenHeader` are defined **directly in the configJSON** (`FormSchema`). When the form is parsed, these values are stored in the `SmartFormController` service and automatically applied to every internal HTTP call — no manual `@Input` wiring in the parent template is needed.
|
|
2079
|
-
|
|
2080
|
-
```json
|
|
2081
|
-
{
|
|
2082
|
-
"entityType": "USER",
|
|
2083
|
-
"label": "User Form",
|
|
2084
|
-
"formType": "SECTION",
|
|
2085
|
-
"token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
2086
|
-
"tokenHeader": "Authorization",
|
|
2087
|
-
"sectionConfig": { "children": [...] }
|
|
2088
|
-
}
|
|
2089
|
-
```
|
|
2090
|
-
|
|
2091
|
-
> **Note:** `tokenHeader` is optional. When omitted it defaults to `"Authorization"`.
|
|
2092
|
-
|
|
2093
|
-
### Template
|
|
2094
|
-
|
|
2095
|
-
No extra bindings are required on `<lib-smart-form>`. The token flows entirely through the JSON:
|
|
2096
|
-
|
|
2097
|
-
```html
|
|
2098
|
-
<lib-smart-form
|
|
2099
|
-
[formJson]="formJson"
|
|
2100
|
-
[labels]="labels"
|
|
2101
|
-
(submit)="onSubmit($event)"
|
|
2102
|
-
>
|
|
2103
|
-
</lib-smart-form>
|
|
2104
|
-
```
|
|
2105
|
-
|
|
2106
|
-
### How the Token Flows Internally
|
|
2107
|
-
|
|
2108
|
-
```
|
|
2109
|
-
FormSchema JSON
|
|
2110
|
-
│ token / tokenHeader parsed by SmartFormComponent
|
|
2111
|
-
▼
|
|
2112
|
-
SmartFormController (token & tokenHeader stored here)
|
|
2113
|
-
│ read by every FormFieldComponent directly
|
|
2114
|
-
▼
|
|
2115
|
-
HttpHeaders → every API call (dropdowns, submit)
|
|
2116
|
-
```
|
|
2117
|
-
|
|
2118
|
-
The token propagates automatically through the entire component tree without any `@Input` prop-drilling.
|
|
2119
|
-
|
|
2120
|
-
### Per-Submit Override
|
|
2121
|
-
|
|
2122
|
-
If your submit endpoint requires a **different** token than the option-loading endpoints, set it directly inside `submitConfig`:
|
|
2123
|
-
|
|
2124
|
-
```json
|
|
2125
|
-
{
|
|
2126
|
-
"submitConfig": {
|
|
2127
|
-
"apiUrl": "https://api.example.com/users",
|
|
2128
|
-
"method": "POST",
|
|
2129
|
-
"token": "ApiKey my-write-only-key",
|
|
2130
|
-
"tokenHeader": "X-API-Key"
|
|
2131
|
-
}
|
|
2132
|
-
}
|
|
2133
|
-
```
|
|
2134
|
-
|
|
2135
|
-
`submitConfig.token` always takes precedence over the schema-level `token` for the submit call only.
|
|
2136
|
-
|
|
2137
|
-
### Priority Order
|
|
2138
|
-
|
|
2139
|
-
```
|
|
2140
|
-
submitConfig.token → FormSchema.token → (no header sent)
|
|
2141
|
-
```
|
|
2142
|
-
|
|
2143
|
-
---
|
|
2144
|
-
|
|
2145
|
-
## Theme Configuration
|
|
2146
|
-
|
|
2147
|
-
The Smart Form exposes a rich set of CSS custom properties (variables) that allow you to fully customise the appearance without touching any component SCSS.
|
|
2148
|
-
|
|
2149
|
-
Two built-in themes are provided via a Sass mixin in `smart-form.theme.scss`.
|
|
2150
|
-
|
|
2151
|
-
### Available Themes
|
|
2152
|
-
|
|
2153
|
-
| Theme constant | Description |
|
|
2154
|
-
| ---------------------------- | ------------------------------------------------ |
|
|
2155
|
-
| `$default-smart-form-config` | **Theme 1** — Clean & Professional (light, blue) |
|
|
2156
|
-
| `$theme-2-smart-form-config` | **Theme 2** — Vibrant & Modern (dark slate) |
|
|
2157
|
-
|
|
2158
|
-
### Applying a Theme
|
|
2159
|
-
|
|
2160
|
-
In your consuming application's component SCSS, `@use` the theme file and call the mixin inside a wrapper class:
|
|
2161
|
-
|
|
2162
|
-
```scss
|
|
2163
|
-
@use "commons-shared-web-ui/src/lib/modules/smart-form/smart-form.theme" as sf;
|
|
2164
|
-
|
|
2165
|
-
// Default (light) theme
|
|
2166
|
-
.my-form-wrapper {
|
|
2167
|
-
@include sf.smart-form-theme();
|
|
2168
|
-
}
|
|
2169
|
-
|
|
2170
|
-
// Dark theme
|
|
2171
|
-
.my-form-wrapper.dark {
|
|
2172
|
-
@include sf.smart-form-theme(sf.$theme-2-smart-form-config);
|
|
2173
|
-
}
|
|
2174
|
-
```
|
|
2175
|
-
|
|
2176
|
-
```html
|
|
2177
|
-
<div class="my-form-wrapper" [class.dark]="isDarkTheme">
|
|
2178
|
-
<lib-smart-form [formJson]="formJson" (submit)="onSubmit($event)">
|
|
2179
|
-
</lib-smart-form>
|
|
2180
|
-
</div>
|
|
2181
|
-
```
|
|
2182
|
-
|
|
2183
|
-
### Custom Theme
|
|
2184
|
-
|
|
2185
|
-
Pass a partial override map to `smart-form-theme()` to produce a custom theme without affecting the defaults:
|
|
2186
|
-
|
|
2187
|
-
```scss
|
|
2188
|
-
@use "commons-shared-web-ui/src/lib/modules/smart-form/smart-form.theme" as sf;
|
|
2189
|
-
|
|
2190
|
-
.my-brand-form {
|
|
2191
|
-
@include sf.smart-form-theme(
|
|
2192
|
-
(
|
|
2193
|
-
form-bg: #f4f7ff,
|
|
2194
|
-
input-focus-border: #7c3aed,
|
|
2195
|
-
input-focus-shadow: 0 0 0 3px rgba(124, 58, 237, 0.2),
|
|
2196
|
-
btn-primary-bg: #7c3aed,
|
|
2197
|
-
btn-primary-hover-bg: #6d28d9,
|
|
2198
|
-
)
|
|
2199
|
-
);
|
|
2200
|
-
}
|
|
2201
|
-
```
|
|
2202
|
-
|
|
2203
|
-
### CSS Custom Properties Reference
|
|
2204
|
-
|
|
2205
|
-
All variables are prefixed `--cc-sf-`. They are generated by the mixin and cascade into every child component automatically.
|
|
2206
|
-
|
|
2207
|
-
#### Form Container
|
|
2208
|
-
|
|
2209
|
-
| Variable | Default (Theme 1) | Description |
|
|
2210
|
-
| ---------------------------- | ---------------------------- | ------------------------------- |
|
|
2211
|
-
| `--cc-sf-font-family` | `'Inter', sans-serif` | Font family for the entire form |
|
|
2212
|
-
| `--cc-sf-font-size-base` | `0.875rem` | Base font size |
|
|
2213
|
-
| `--cc-sf-form-bg` | `#ffffff` | Form wrapper background |
|
|
2214
|
-
| `--cc-sf-form-border-radius` | `12px` | Form wrapper border radius |
|
|
2215
|
-
| `--cc-sf-form-border` | `none` | Form wrapper border |
|
|
2216
|
-
| `--cc-sf-form-shadow` | `0 1px 3px rgba(0,0,0,0.06)` | Form wrapper box shadow |
|
|
2217
|
-
| `--cc-sf-form-padding` | `24px` | Form outer padding |
|
|
2218
|
-
| `--cc-sf-form-max-width` | `1200px` | Form max width |
|
|
2219
|
-
| `--cc-sf-form-title-color` | `#111827` | Form title color |
|
|
2220
|
-
| `--cc-sf-form-title-size` | `1.5rem` | Form title font size |
|
|
2221
|
-
| `--cc-sf-form-title-weight` | `700` | Form title font weight |
|
|
2222
|
-
| `--cc-sf-form-desc-color` | `#6B7280` | Form description color |
|
|
2223
|
-
| `--cc-sf-form-desc-size` | `0.875rem` | Form description font size |
|
|
2224
|
-
|
|
2225
|
-
#### Section / GROUP Card
|
|
2226
|
-
|
|
2227
|
-
| Variable | Default (Theme 1) | Description |
|
|
2228
|
-
| ------------------------------ | ------------------- | ----------------------------- |
|
|
2229
|
-
| `--cc-sf-section-bg` | `#ffffff` | Section card background |
|
|
2230
|
-
| `--cc-sf-section-border` | `1px solid #E5E7EB` | Section card border |
|
|
2231
|
-
| `--cc-sf-section-radius` | `10px` | Section card border radius |
|
|
2232
|
-
| `--cc-sf-section-shadow` | `0 1px 4px rgba(…)` | Section card box shadow |
|
|
2233
|
-
| `--cc-sf-section-padding` | `20px` | Section card inner padding |
|
|
2234
|
-
| `--cc-sf-section-gap` | `20px` | Gap between sections |
|
|
2235
|
-
| `--cc-sf-section-label-color` | `#1F2937` | Section heading color |
|
|
2236
|
-
| `--cc-sf-section-label-size` | `1rem` | Section heading font size |
|
|
2237
|
-
| `--cc-sf-section-label-weight` | `600` | Section heading font weight |
|
|
2238
|
-
| `--cc-sf-section-label-border` | `2px solid #E5E7EB` | Section heading bottom border |
|
|
2239
|
-
|
|
2240
|
-
#### Grid
|
|
2241
|
-
|
|
2242
|
-
| Variable | Default (Theme 1) | Description |
|
|
2243
|
-
| ---------------------- | ----------------- | ---------------------------------- |
|
|
2244
|
-
| `--cc-sf-grid-gap` | `16px` | Gap between columns in all grids |
|
|
2245
|
-
| `--cc-sf-grid-row-gap` | `12px` | Gap between rows in section grid |
|
|
2246
|
-
|
|
2247
|
-
#### Labels
|
|
2248
|
-
|
|
2249
|
-
| Variable | Default (Theme 1) | Description |
|
|
2250
|
-
| ------------------------------ | ----------------- | ---------------------------------- |
|
|
2251
|
-
| `--cc-sf-label-color` | `#111827` | Field label text color |
|
|
2252
|
-
| `--cc-sf-label-size` | `0.875rem` | Field label font size |
|
|
2253
|
-
| `--cc-sf-label-weight` | `500` | Field label font weight |
|
|
2254
|
-
| `--cc-sf-label-required-color` | `#DC2626` | Required asterisk color |
|
|
2255
|
-
| `--cc-sf-label-mb` | `0px` | Field label bottom margin offset |
|
|
2256
|
-
|
|
2257
|
-
#### Inputs
|
|
2258
|
-
|
|
2259
|
-
| Variable | Default (Theme 1) | Description |
|
|
2260
|
-
| ------------------------------- | --------------------------------- | ------------------------- |
|
|
2261
|
-
| `--cc-sf-input-bg` | `#ffffff` | Input background |
|
|
2262
|
-
| `--cc-sf-input-color` | `#111827` | Input text color |
|
|
2263
|
-
| `--cc-sf-input-placeholder` | `#9CA3AF` | Placeholder text color |
|
|
2264
|
-
| `--cc-sf-input-border` | `1.5px solid #D1D5DB` | Input border |
|
|
2265
|
-
| `--cc-sf-input-radius` | `8px` | Input border radius |
|
|
2266
|
-
| `--cc-sf-input-padding` | `0.625rem 0.875rem` | Input padding |
|
|
2267
|
-
| `--cc-sf-input-shadow` | `none` | Input default box shadow |
|
|
2268
|
-
| `--cc-sf-input-hover-border` | `#9CA3AF` | Input border on hover |
|
|
2269
|
-
| `--cc-sf-input-focus-border` | `#3B82F6` | Input border when focused |
|
|
2270
|
-
| `--cc-sf-input-focus-shadow` | `0 0 0 3px rgba(59,130,246,0.12)` | Focus ring glow |
|
|
2271
|
-
| `--cc-sf-input-disabled-bg` | `#F3F4F6` | Disabled input background |
|
|
2272
|
-
| `--cc-sf-input-disabled-color` | `#6B7280` | Disabled input text color |
|
|
2273
|
-
| `--cc-sf-input-disabled-border` | `#E5E7EB` | Disabled input border |
|
|
2274
|
-
| `--cc-sf-input-transition` | `all 0.2s ease` | Input transition speed |
|
|
2275
|
-
|
|
2276
|
-
#### Validation
|
|
2277
|
-
|
|
2278
|
-
| Variable | Default (Theme 1) | Description |
|
|
2279
|
-
| ---------------------------- | ------------------------------- | ------------------------------ |
|
|
2280
|
-
| `--cc-sf-error-border` | `#DC2626` | Error state input border color |
|
|
2281
|
-
| `--cc-sf-error-bg` | `#FEF2F2` | Error state input background |
|
|
2282
|
-
| `--cc-sf-error-focus-shadow` | `0 0 0 3px rgba(220,38,38,0.1)` | Error state focus ring |
|
|
2283
|
-
| `--cc-sf-error-text-color` | `#DC2626` | Error message text color |
|
|
2284
|
-
| `--cc-sf-error-text-size` | `0.75rem` | Error message font size |
|
|
2285
|
-
| `--cc-sf-hint-color` | `#6B7280` | Hint text color |
|
|
2286
|
-
| `--cc-sf-hint-size` | `0.75rem` | Hint text font size |
|
|
2287
|
-
|
|
2288
|
-
#### Chips
|
|
2289
|
-
|
|
2290
|
-
| Variable | Default (Theme 1) | Description |
|
|
2291
|
-
| ------------------------------ | ------------------- | ------------------------ |
|
|
2292
|
-
| `--cc-sf-chip-bg` | `#ffffff` | Chip default background |
|
|
2293
|
-
| `--cc-sf-chip-color` | `#374151` | Chip default text color |
|
|
2294
|
-
| `--cc-sf-chip-border` | `1px solid #D1D5DB` | Chip border |
|
|
2295
|
-
| `--cc-sf-chip-radius` | `20px` | Chip border radius |
|
|
2296
|
-
| `--cc-sf-chip-padding` | `6px 14px` | Chip padding |
|
|
2297
|
-
| `--cc-sf-chip-hover-bg` | `#F3F4F6` | Chip hover background |
|
|
2298
|
-
| `--cc-sf-chip-selected-bg` | `#3B82F6` | Selected chip background |
|
|
2299
|
-
| `--cc-sf-chip-selected-color` | `#ffffff` | Selected chip text |
|
|
2300
|
-
| `--cc-sf-chip-selected-border` | `#3B82F6` | Selected chip border |
|
|
2301
|
-
|
|
2302
|
-
#### Switch
|
|
2303
|
-
|
|
2304
|
-
| Variable | Default (Theme 1) | Description |
|
|
2305
|
-
| -------------------------- | ----------------- | ------------------- |
|
|
2306
|
-
| `--cc-sf-switch-track-on` | `#3B82F6` | Toggle track (on) |
|
|
2307
|
-
| `--cc-sf-switch-track-off` | `#D1D5DB` | Toggle track (off) |
|
|
2308
|
-
| `--cc-sf-switch-thumb` | `#ffffff` | Toggle thumb colour |
|
|
2309
|
-
|
|
2310
|
-
#### Rating Stars
|
|
2311
|
-
|
|
2312
|
-
| Variable | Default (Theme 1) | Description |
|
|
2313
|
-
| --------------------- | ----------------- | ------------------ |
|
|
2314
|
-
| `--cc-sf-star-filled` | `#F59E0B` | Filled star colour |
|
|
2315
|
-
| `--cc-sf-star-empty` | `#D1D5DB` | Empty star colour |
|
|
2316
|
-
| `--cc-sf-star-size` | `28px` | Star font size |
|
|
2317
|
-
|
|
2318
|
-
#### File Upload Drop Zone
|
|
2319
|
-
|
|
2320
|
-
| Variable | Default (Theme 1) | Description |
|
|
2321
|
-
| ------------------------------- | ---------------------- | ---------------------------- |
|
|
2322
|
-
| `--cc-sf-dropzone-bg` | `#FFFAF1` | Drop zone background |
|
|
2323
|
-
| `--cc-sf-dropzone-border` | `1.5px dashed #CBD5E1` | Drop zone border |
|
|
2324
|
-
| `--cc-sf-dropzone-radius` | `12px` | Drop zone border radius |
|
|
2325
|
-
| `--cc-sf-dropzone-hover-bg` | `#EFF6FF` | Hover background |
|
|
2326
|
-
| `--cc-sf-dropzone-hover-border` | `#93C5FD` | Hover border |
|
|
2327
|
-
| `--cc-sf-dropzone-over-border` | `#3B82F6` | Active drag-over border |
|
|
2328
|
-
| `--cc-sf-dropzone-icon-color` | `#94A3B8` | Cloud icon colour |
|
|
2329
|
-
| `--cc-sf-dropzone-link-color` | `#3B82F6` | "click to upload" link color |
|
|
2330
|
-
| `--cc-sf-dropzone-hint-color` | `#64748B` | Hint text colour in zone |
|
|
2331
|
-
| `--cc-sf-dropzone-over-shadow` | `0 0 0 4px rgba(…)` | Drag-over glow |
|
|
2332
|
-
|
|
2333
|
-
#### Stepper
|
|
2334
|
-
|
|
2335
|
-
| Variable | Default (Theme 1) | Description |
|
|
2336
|
-
| ---------------------------------- | ----------------- | -------------------------------- |
|
|
2337
|
-
| `--cc-sf-step-number-size` | `40px` | Step bubble size |
|
|
2338
|
-
| `--cc-sf-step-number-bg` | `#E5E7EB` | Idle step bubble background |
|
|
2339
|
-
| `--cc-sf-step-number-color` | `#6B7280` | Idle step number colour |
|
|
2340
|
-
| `--cc-sf-step-number-font-size` | `0.875rem` | Step number font size |
|
|
2341
|
-
| `--cc-sf-step-number-weight` | `600` | Step number font weight |
|
|
2342
|
-
| `--cc-sf-step-label-color` | `#6B7280` | Idle step label colour |
|
|
2343
|
-
| `--cc-sf-step-label-size` | `0.875rem` | Step label font size |
|
|
2344
|
-
| `--cc-sf-step-label-weight` | `500` | Step label font weight |
|
|
2345
|
-
| `--cc-sf-step-active-bg` | `#3B82F6` | Active step bubble background |
|
|
2346
|
-
| `--cc-sf-step-active-color` | `#ffffff` | Active step number colour |
|
|
2347
|
-
| `--cc-sf-step-active-label` | `#1D4ED8` | Active step label colour |
|
|
2348
|
-
| `--cc-sf-step-active-label-weight` | `700` | Active step label font weight |
|
|
2349
|
-
| `--cc-sf-step-done-bg` | `#22C55E` | Completed step bubble background |
|
|
2350
|
-
| `--cc-sf-step-done-color` | `#ffffff` | Completed step number colour |
|
|
2351
|
-
| `--cc-sf-step-connector-color` | `#E5E7EB` | Connector line (idle) |
|
|
2352
|
-
| `--cc-sf-step-connector-done` | `#22C55E` | Connector line (completed) |
|
|
2353
|
-
|
|
2354
|
-
#### Action Buttons
|
|
2355
|
-
|
|
2356
|
-
| Variable | Default (Theme 1) | Description |
|
|
2357
|
-
| -------------------------------- | -------------------- | ---------------------------------- |
|
|
2358
|
-
| `--cc-sf-btn-primary-bg` | `#3B82F6` | Primary (submit) button background |
|
|
2359
|
-
| `--cc-sf-btn-primary-color` | `#ffffff` | Primary button text colour |
|
|
2360
|
-
| `--cc-sf-btn-primary-radius` | `8px` | Primary button border radius |
|
|
2361
|
-
| `--cc-sf-btn-primary-padding` | `0.625rem 1.5rem` | Primary button padding |
|
|
2362
|
-
| `--cc-sf-btn-primary-hover-bg` | `#2563EB` | Primary button hover background |
|
|
2363
|
-
| `--cc-sf-btn-secondary-bg` | `#F3F4F6` | Secondary (previous) button bg |
|
|
2364
|
-
| `--cc-sf-btn-secondary-color` | `#374151` | Secondary button text colour |
|
|
2365
|
-
| `--cc-sf-btn-secondary-radius` | `8px` | Secondary button border radius |
|
|
2366
|
-
| `--cc-sf-btn-secondary-padding` | `0.625rem 1.5rem` | Secondary button padding |
|
|
2367
|
-
| `--cc-sf-btn-secondary-hover-bg` | `#E5E7EB` | Secondary button hover background |
|
|
2368
|
-
| `--cc-sf-btn-disabled-opacity` | `0.55` | Disabled button opacity |
|
|
2369
|
-
| `--cc-sf-btn-font-size` | `0.875rem` | Button font size |
|
|
2370
|
-
| `--cc-sf-btn-font-weight` | `600` | Button font weight |
|
|
2371
|
-
| `--cc-sf-btn-transition` | `all 0.2s ease` | Button transition |
|
|
2372
|
-
| `--cc-sf-actions-gap` | `12px` | Gap between action buttons |
|
|
2373
|
-
| `--cc-sf-actions-padding` | `20px 0 0` | Action bar padding |
|
|
2374
|
-
| `--cc-sf-actions-border` | `1px solid #E5E7EB` | Action bar top border |
|
|
2375
|
-
| `--cc-sf-btn-add-bg` | `transparent` | Add-instance button background |
|
|
2376
|
-
| `--cc-sf-btn-add-color` | `#3B82F6` | Add-instance button text colour |
|
|
2377
|
-
| `--cc-sf-btn-add-border` | `1px dashed #CBD5E1` | Add-instance button border |
|
|
2378
|
-
| `--cc-sf-btn-add-radius` | `6px` | Add-instance button radius |
|
|
2379
|
-
| `--cc-sf-btn-add-hover-bg` | `#EFF6FF` | Add-instance button hover bg |
|
|
2380
|
-
| `--cc-sf-btn-add-hover-border` | `#BFDBFE` | Add-instance button hover border |
|
|
2381
|
-
| `--cc-sf-btn-remove-bg` | `#FFF5F5` | Remove-instance button background |
|
|
2382
|
-
| `--cc-sf-btn-remove-color` | `#E53E3E` | Remove-instance button text colour |
|
|
2383
|
-
| `--cc-sf-btn-remove-border` | `1px solid #FED7D7` | Remove-instance button border |
|
|
2384
|
-
| `--cc-sf-btn-remove-radius` | `4px` | Remove-instance button radius |
|
|
2385
|
-
| `--cc-sf-btn-remove-hover-bg` | `#FED7D7` | Remove-instance button hover bg |
|
|
2386
|
-
|
|
2387
|
-
---
|
|
2388
|
-
|
|
2389
|
-
## Examples
|
|
2390
|
-
|
|
2391
|
-
### Contact Form
|
|
2392
|
-
|
|
2393
|
-
```json
|
|
2394
|
-
{
|
|
2395
|
-
"entityType": "CONTACT",
|
|
2396
|
-
"label": "Contact Us",
|
|
2397
|
-
"formType": "SECTION",
|
|
2398
|
-
"submitConfig": {
|
|
2399
|
-
"apiUrl": "https://api.example.com/contact",
|
|
2400
|
-
"method": "POST",
|
|
2401
|
-
"successMessage": "Message sent successfully",
|
|
2402
|
-
"errorMessage": "Failed to send message",
|
|
2403
|
-
"snackbarConfig": {
|
|
2404
|
-
"duration": 6000,
|
|
2405
|
-
"verticalPosition": "top",
|
|
2406
|
-
"horizontalPosition": "center",
|
|
2407
|
-
"showCloseButton": true
|
|
2408
|
-
}
|
|
2409
|
-
},
|
|
2410
|
-
"sectionConfig": {
|
|
2411
|
-
"children": [
|
|
2412
|
-
{
|
|
2413
|
-
"name": "name",
|
|
2414
|
-
"label": "Full Name",
|
|
2415
|
-
"type": "TEXT_INPUT",
|
|
2416
|
-
"subType": "SHORT",
|
|
2417
|
-
"required": true
|
|
2418
|
-
},
|
|
2419
|
-
{
|
|
2420
|
-
"name": "email",
|
|
2421
|
-
"label": "Email Address",
|
|
2422
|
-
"type": "TEXT_INPUT",
|
|
2423
|
-
"subType": "EMAIL",
|
|
2424
|
-
"required": true
|
|
2425
|
-
},
|
|
2426
|
-
{
|
|
2427
|
-
"name": "message",
|
|
2428
|
-
"label": "Message",
|
|
2429
|
-
"type": "TEXT_INPUT",
|
|
2430
|
-
"subType": "LONG",
|
|
2431
|
-
"required": true,
|
|
2432
|
-
"textConfig": { "length": { "min": 10, "max": 500 } }
|
|
2433
|
-
}
|
|
2434
|
-
]
|
|
2435
|
-
}
|
|
2436
|
-
}
|
|
2437
|
-
```
|
|
2438
|
-
|
|
2439
|
-
### User Registration with Password Validation
|
|
2440
|
-
|
|
2441
|
-
```json
|
|
2442
|
-
{
|
|
2443
|
-
"entityType": "USER_REGISTRATION",
|
|
2444
|
-
"label": "User Registration",
|
|
2445
|
-
"formType": "SECTION",
|
|
2446
|
-
"sectionConfig": {
|
|
2447
|
-
"children": [
|
|
2448
|
-
{
|
|
2449
|
-
"type": "GROUP",
|
|
2450
|
-
"subType": "SECTION",
|
|
2451
|
-
"sectionConfig": {
|
|
2452
|
-
"label": "Credentials",
|
|
2453
|
-
"name": "credentials",
|
|
2454
|
-
"children": [
|
|
2455
|
-
{
|
|
2456
|
-
"type": "ROW",
|
|
2457
|
-
"subType": "HORIZONTAL",
|
|
2458
|
-
"children": [
|
|
2459
|
-
{
|
|
2460
|
-
"name": "loginId",
|
|
2461
|
-
"label": "Login ID",
|
|
2462
|
-
"type": "TEXT_INPUT",
|
|
2463
|
-
"subType": "SHORT",
|
|
2464
|
-
"required": true,
|
|
2465
|
-
"colSpan": 4
|
|
2466
|
-
},
|
|
2467
|
-
{
|
|
2468
|
-
"name": "password",
|
|
2469
|
-
"label": "Password",
|
|
2470
|
-
"type": "TEXT_INPUT",
|
|
2471
|
-
"subType": "PASSWORD",
|
|
2472
|
-
"required": true,
|
|
2473
|
-
"colSpan": 4
|
|
2474
|
-
},
|
|
2475
|
-
{
|
|
2476
|
-
"name": "confirmPassword",
|
|
2477
|
-
"label": "Confirm Password",
|
|
2478
|
-
"type": "TEXT_INPUT",
|
|
2479
|
-
"subType": "PASSWORD",
|
|
2480
|
-
"required": true,
|
|
2481
|
-
"colSpan": 4,
|
|
2482
|
-
"textConfig": { "matchField": "password" }
|
|
2483
|
-
}
|
|
2484
|
-
]
|
|
2485
|
-
}
|
|
2486
|
-
]
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
]
|
|
2490
|
-
}
|
|
2491
|
-
}
|
|
2492
|
-
```
|
|
2493
|
-
|
|
2494
|
-
### Section Stepper with `showOnLastStepOnly`
|
|
2495
|
-
|
|
2496
|
-
A three-step registration form using Section Stepper. The **Submit** button carries `"showOnLastStepOnly": true` so it is only visible on the last step. **Previous** and **Next** are rendered automatically by the form — no extra config needed.
|
|
2497
|
-
|
|
2498
|
-
```json
|
|
2499
|
-
{
|
|
2500
|
-
"entityType": "REGISTRATION",
|
|
2501
|
-
"label": "Event Registration",
|
|
2502
|
-
"formType": "SECTION",
|
|
2503
|
-
"sectionStepper": true,
|
|
2504
|
-
"submitConfig": {
|
|
2505
|
-
"apiUrl": "https://api.example.com/registrations",
|
|
2506
|
-
"method": "POST",
|
|
2507
|
-
"successMessage": "Registration submitted successfully!",
|
|
2508
|
-
"redirectUrl": "/dashboard"
|
|
2509
|
-
},
|
|
2510
|
-
"actionBarConfig": {
|
|
2511
|
-
"buttons": [
|
|
2512
|
-
{
|
|
2513
|
-
"id": "cancel",
|
|
2514
|
-
"label": "Cancel",
|
|
2515
|
-
"variant": "outline",
|
|
2516
|
-
"alignment": "left",
|
|
2517
|
-
"order": 0,
|
|
2518
|
-
"action": { "kind": "navigate", "redirectUrl": "/dashboard" }
|
|
2519
|
-
},
|
|
2520
|
-
{
|
|
2521
|
-
"id": "save-draft",
|
|
2522
|
-
"label": "Save as Draft",
|
|
2523
|
-
"variant": "secondary",
|
|
2524
|
-
"alignment": "right",
|
|
2525
|
-
"order": 0,
|
|
2526
|
-
"action": { "kind": "draft", "extraPayload": { "status": "DRAFT" } }
|
|
2527
|
-
},
|
|
2528
|
-
{
|
|
2529
|
-
"id": "submit",
|
|
2530
|
-
"label": "Submit",
|
|
2531
|
-
"variant": "primary",
|
|
2532
|
-
"alignment": "right",
|
|
2533
|
-
"order": 1,
|
|
2534
|
-
"showOnLastStepOnly": true,
|
|
2535
|
-
"action": { "kind": "submit" }
|
|
2536
|
-
}
|
|
2537
|
-
]
|
|
2538
|
-
},
|
|
2539
|
-
"sectionConfig": {
|
|
2540
|
-
"children": [
|
|
2541
|
-
{
|
|
2542
|
-
"type": "GROUP",
|
|
2543
|
-
"subType": "SECTION",
|
|
2544
|
-
"sectionConfig": {
|
|
2545
|
-
"label": "Personal Info",
|
|
2546
|
-
"children": [
|
|
2547
|
-
{
|
|
2548
|
-
"type": "ROW",
|
|
2549
|
-
"subType": "HORIZONTAL",
|
|
2550
|
-
"children": [
|
|
2551
|
-
{
|
|
2552
|
-
"name": "firstName",
|
|
2553
|
-
"label": "First Name",
|
|
2554
|
-
"type": "TEXT_INPUT",
|
|
2555
|
-
"subType": "SHORT",
|
|
2556
|
-
"required": true,
|
|
2557
|
-
"colSpan": 6
|
|
2558
|
-
},
|
|
2559
|
-
{
|
|
2560
|
-
"name": "lastName",
|
|
2561
|
-
"label": "Last Name",
|
|
2562
|
-
"type": "TEXT_INPUT",
|
|
2563
|
-
"subType": "SHORT",
|
|
2564
|
-
"required": true,
|
|
2565
|
-
"colSpan": 6
|
|
2566
|
-
}
|
|
2567
|
-
]
|
|
2568
|
-
},
|
|
2569
|
-
{
|
|
2570
|
-
"name": "email",
|
|
2571
|
-
"label": "Email Address",
|
|
2572
|
-
"type": "TEXT_INPUT",
|
|
2573
|
-
"subType": "EMAIL",
|
|
2574
|
-
"required": true
|
|
2575
|
-
}
|
|
2576
|
-
]
|
|
2577
|
-
}
|
|
2578
|
-
},
|
|
2579
|
-
{
|
|
2580
|
-
"type": "GROUP",
|
|
2581
|
-
"subType": "SECTION",
|
|
2582
|
-
"sectionConfig": {
|
|
2583
|
-
"label": "Event Preferences",
|
|
2584
|
-
"children": [
|
|
2585
|
-
{
|
|
2586
|
-
"name": "session",
|
|
2587
|
-
"label": "Preferred Session",
|
|
2588
|
-
"type": "DROPDOWN",
|
|
2589
|
-
"subType": "SINGLE",
|
|
2590
|
-
"required": true,
|
|
2591
|
-
"optionConfig": {
|
|
2592
|
-
"optionList": [
|
|
2593
|
-
{ "label": "Morning", "code": "MORNING" },
|
|
2594
|
-
{ "label": "Afternoon", "code": "AFTERNOON" },
|
|
2595
|
-
{ "label": "Evening", "code": "EVENING" }
|
|
2596
|
-
]
|
|
2597
|
-
}
|
|
2598
|
-
},
|
|
2599
|
-
{
|
|
2600
|
-
"name": "dietaryRequirements",
|
|
2601
|
-
"label": "Dietary Requirements",
|
|
2602
|
-
"type": "CHIP",
|
|
2603
|
-
"subType": "MULTIPLE",
|
|
2604
|
-
"optionConfig": {
|
|
2605
|
-
"optionList": [
|
|
2606
|
-
{ "label": "Vegetarian", "code": "VEG" },
|
|
2607
|
-
{ "label": "Vegan", "code": "VEGAN" },
|
|
2608
|
-
{ "label": "Gluten-Free", "code": "GF" },
|
|
2609
|
-
{ "label": "None", "code": "NONE" }
|
|
2610
|
-
]
|
|
2611
|
-
}
|
|
2612
|
-
}
|
|
2613
|
-
]
|
|
2614
|
-
}
|
|
2615
|
-
},
|
|
2616
|
-
{
|
|
2617
|
-
"type": "GROUP",
|
|
2618
|
-
"subType": "SECTION",
|
|
2619
|
-
"sectionConfig": {
|
|
2620
|
-
"label": "Review & Submit",
|
|
2621
|
-
"children": [
|
|
2622
|
-
{
|
|
2623
|
-
"name": "agreeToTerms",
|
|
2624
|
-
"label": "I agree to the terms and conditions",
|
|
2625
|
-
"type": "CHECKBOX",
|
|
2626
|
-
"subType": "BOOL",
|
|
2627
|
-
"required": true
|
|
2628
|
-
},
|
|
2629
|
-
{
|
|
2630
|
-
"name": "notes",
|
|
2631
|
-
"label": "Additional Notes",
|
|
2632
|
-
"type": "TEXT_INPUT",
|
|
2633
|
-
"subType": "LONG"
|
|
2634
|
-
}
|
|
2635
|
-
]
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2638
|
-
]
|
|
2639
|
-
}
|
|
2640
|
-
}
|
|
2641
|
-
```
|
|
2642
|
-
|
|
2643
|
-
**What the action bar looks like on each step:**
|
|
2644
|
-
|
|
2645
|
-
| Step | Left side | Right side |
|
|
2646
|
-
|------|-----------|------------|
|
|
2647
|
-
| Step 1 (first) | Cancel | Save as Draft · **Next** |
|
|
2648
|
-
| Step 2 | Cancel · **Previous** | Save as Draft · **Next** |
|
|
2649
|
-
| Step 3 (last) | Cancel · **Previous** | Save as Draft · **Submit** |
|
|
2650
|
-
|
|
2651
|
-
- **Previous** / **Next** are rendered automatically; no button config needed.
|
|
2652
|
-
- **Submit** (`showOnLastStepOnly: true`) is hidden on steps 1 and 2, then replaces **Next** on step 3.
|
|
2653
|
-
- **Cancel** and **Save as Draft** have no `showOnLastStepOnly` flag, so they appear on every step.
|
|
2654
|
-
|
|
2655
|
-
### Repeatable Work Experience
|
|
2656
|
-
|
|
2657
|
-
```json
|
|
2658
|
-
{
|
|
2659
|
-
"type": "GROUP",
|
|
2660
|
-
"subType": "SECTION",
|
|
2661
|
-
"sectionConfig": {
|
|
2662
|
-
"label": "Work Experience",
|
|
2663
|
-
"allowMulti": true,
|
|
2664
|
-
"name": "experienceList",
|
|
2665
|
-
"children": [
|
|
2666
|
-
{
|
|
2667
|
-
"name": "company",
|
|
2668
|
-
"label": "Company",
|
|
2669
|
-
"type": "TEXT_INPUT",
|
|
2670
|
-
"subType": "SHORT",
|
|
2671
|
-
"required": true
|
|
2672
|
-
},
|
|
2673
|
-
{
|
|
2674
|
-
"name": "position",
|
|
2675
|
-
"label": "Position",
|
|
2676
|
-
"type": "TEXT_INPUT",
|
|
2677
|
-
"subType": "SHORT",
|
|
2678
|
-
"required": true
|
|
2679
|
-
},
|
|
2680
|
-
{
|
|
2681
|
-
"type": "ROW",
|
|
2682
|
-
"subType": "HORIZONTAL",
|
|
2683
|
-
"children": [
|
|
2684
|
-
{
|
|
2685
|
-
"name": "startDate",
|
|
2686
|
-
"label": "Start Date",
|
|
2687
|
-
"type": "DATE",
|
|
2688
|
-
"subType": "SINGLE",
|
|
2689
|
-
"required": true,
|
|
2690
|
-
"colSpan": 6
|
|
2691
|
-
},
|
|
2692
|
-
{
|
|
2693
|
-
"name": "endDate",
|
|
2694
|
-
"label": "End Date",
|
|
2695
|
-
"type": "DATE",
|
|
2696
|
-
"subType": "SINGLE",
|
|
2697
|
-
"colSpan": 6
|
|
2698
|
-
}
|
|
2699
|
-
]
|
|
2700
|
-
}
|
|
2701
|
-
]
|
|
2702
|
-
}
|
|
2703
|
-
}
|
|
2704
|
-
```
|
|
2705
|
-
|
|
2706
|
-
---
|
|
2707
|
-
|
|
2708
|
-
## API Reference
|
|
2709
|
-
|
|
2710
|
-
### Component Inputs
|
|
2711
|
-
|
|
2712
|
-
| Input | Type | Required | Description |
|
|
2713
|
-
| --------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
2714
|
-
| `formJson` | string | Yes | JSON string of the form schema configuration |
|
|
2715
|
-
| `mode` | string | No | Determines whether the form creates or edits an entity: `"CREATE" \| "EDIT"` (default: `"CREATE"`) |
|
|
2716
|
-
| `initialValues` | object | No | Pre-populate form values (key = field `name`) |
|
|
2717
|
-
| `enableDraftAutoSave` | boolean | No | Enable auto-save drafts (default: `false`) |
|
|
2718
|
-
| `labels` | object | No | Flat i18n key–value map. Keys must match the string values used in the JSON schema. See [i18n section](#i18n--translation-support). |
|
|
2719
|
-
| `readOnly` | boolean | No | When `true`, all form controls are disabled and the action bar is hidden. Use this for **read-only preview** in a Form Builder wizard (default: `false`). |
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
### Component Outputs
|
|
2723
|
-
|
|
2724
|
-
| `submit` | `EventEmitter<object>` | Emitted when the form is submitted. Contains collected form data or the API response |
|
|
2725
|
-
| `draftSave` | `EventEmitter<string>` | Emitted when a draft is saved (requires `enableDraftAutoSave: true`) |
|
|
2726
|
-
| `fileAdded` | `EventEmitter<any>` | Emitted when a file upload starts internally (MEDIA_UPLOAD or FILE_UPLOAD). |
|
|
2727
|
-
| `fileRemoved` | `EventEmitter<any>` | Emitted when a file upload completes, fails, or is manually removed. |
|
|
2728
|
-
| `suffixActionClick` | `EventEmitter<{ fieldName: string; actionId: string }>` | Emitted when a clickable action icon (configured via `suffixActionIcons`) is clicked inside a text or number field. |
|
|
2729
|
-
|
|
2730
|
-
### Usage in Component
|
|
2731
|
-
|
|
2732
|
-
```typescript
|
|
2733
|
-
@Component({
|
|
2734
|
-
template: `
|
|
2735
|
-
<lib-smart-form
|
|
2736
|
-
[formJson]="formJson"
|
|
2737
|
-
[initialValues]="initialData"
|
|
2738
|
-
[labels]="labels"
|
|
2739
|
-
(submit)="handleSubmit($event)"
|
|
2740
|
-
>
|
|
2741
|
-
</lib-smart-form>
|
|
2742
|
-
`,
|
|
2743
|
-
})
|
|
2744
|
-
export class MyComponent {
|
|
2745
|
-
formJson: string; // JSON.stringify(yourSchemaObject)
|
|
2746
|
-
initialData = { firstName: "John" };
|
|
2747
|
-
labels = { "FIELD.FIRST_NAME": "First Name" };
|
|
2748
|
-
|
|
2749
|
-
handleSubmit(data: any) {
|
|
2750
|
-
console.log("Form Data:", data);
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
```
|
|
2754
|
-
|
|
2755
|
-
---
|
|
2756
|
-
|
|
2757
|
-
## Best Practices
|
|
2758
|
-
|
|
2759
|
-
1. Use **meaningful camelCase field names** (`firstName`, `emailId`).
|
|
2760
|
-
2. Store all label strings in your **i18n JSON files** and pass them via `[labels]`. Every label property in the JSON schema should be an i18n key that exists in your translation files. These are processed by `SmartFormTranslationUtils` before rendering.
|
|
2761
|
-
3. Always set `name` on `GROUP` + `allowMulti` sections — it becomes the `FormArray` key.
|
|
2762
|
-
4. Use `colSpan` to build responsive multi-column layouts inside `ROW`.
|
|
2763
|
-
5. Use `visibilityExpression` to keep forms concise — hidden fields are excluded from submission.
|
|
2764
|
-
6. Provide `hints` and `placeholders` for complex fields; these are also translatable.
|
|
2765
|
-
7. Use `matchField` for confirm-password style validation instead of custom validators.
|
|
2766
|
-
8. Test `GENERATED` formulas with `null`/`undefined` inputs — they will be called before the user types.
|
|
2767
|
-
9. For long forms, prefer `STEPPER` over a single `SECTION` with many fields.
|
|
2768
|
-
10. Verify `submitConfig.apiUrl` CORS settings before relying on automatic API submission.
|
|
2769
|
-
11. Always set **`token`** in the configJSON (`FormSchema.token`) when dropdowns load from secured API endpoints — without it the requests will be unauthenticated. The token flows automatically to every field; no `@Input` binding is needed on `<lib-smart-form>`.
|
|
2770
|
-
12. Apply themes using the `smart-form-theme()` mixin inside a scoped wrapper class, not at `:root` level, to avoid conflicts with other form instances on the same page.
|
|
2771
|
-
13. Structure non-repeater section fields directly under `sectionConfig.children` using `colSpan` (`12`, `6`, `4`) rather than wrapping non-repeater fields in unnecessary `ROW` or `GROUP` wrappers. This allows the 12-column `sf-grid` to handle layout automatically and supports field-level collapsing in configurators.
|
|
2772
|
-
|
|
2773
|
-
---
|
|
2774
|
-
|
|
2775
|
-
## Troubleshooting
|
|
2776
|
-
|
|
2777
|
-
### Form not rendering
|
|
2778
|
-
|
|
2779
|
-
- Ensure `formJson` is a valid JSON **string** (use `JSON.stringify`).
|
|
2780
|
-
- Check the browser console for parse errors.
|
|
2781
|
-
|
|
2782
|
-
### Labels not translating
|
|
2783
|
-
|
|
2784
|
-
- Confirm the key in `labels` exactly matches the string value in the schema JSON.
|
|
2785
|
-
- If `labels` arrives after `formJson`, the component automatically re-parses — but make sure `ngOnChanges` is reaching the component (i.e., `labels` is a fresh object reference, not mutated in place).
|
|
2786
|
-
|
|
2787
|
-
### Repeater instances sharing values
|
|
2788
|
-
|
|
2789
|
-
- Ensure `sectionConfig.name` is set on the `GROUP` field — without it the `FormArray` key defaults to `__repeater__` and may conflict.
|
|
2790
|
-
- Each repeater instance starts blank by design (`inRepeater: true` disables cross-instance controller reads).
|
|
2791
|
-
|
|
2792
|
-
### Generated fields not updating
|
|
2793
|
-
|
|
2794
|
-
- Verify `variables` lists every field name the formula reads.
|
|
2795
|
-
- Check formula syntax — it must be a valid **named** JavaScript function.
|
|
2796
|
-
|
|
2797
|
-
### API submission not working
|
|
2798
|
-
|
|
2799
|
-
- Verify `submitConfig.apiUrl` is correct and the server allows the HTTP method.
|
|
2800
|
-
- Check CORS settings on the API endpoint.
|
|
2801
|
-
|
|
2802
|
-
### Dropdown options not loading
|
|
2803
|
-
|
|
2804
|
-
- Verify `apiUrl` / `optionUrl` is reachable.
|
|
2805
|
-
- Check `dataPath` if the response wraps the array in an object.
|
|
2806
|
-
- Verify `labelPath` and `valuePath` exist on each item.
|
|
2807
|
-
- For dependent dropdowns, ensure the parent field name in `dependencies` maps matches the field's `name` exactly.
|
|
2808
|
-
|
|
2809
|
-
### Dropdown options return 401 / 403
|
|
2810
|
-
|
|
2811
|
-
- Set `token` and optionally `tokenHeader` inside the **configJSON** (`FormSchema`) so the library attaches auth headers to every dropdown API call automatically.
|
|
2812
|
-
- If only the **submit** endpoint is secured, set `token` inside `submitConfig` in the JSON schema — it takes precedence over `FormSchema.token` for the submit call only.
|
|
2813
|
-
- Confirm the token value includes the scheme prefix if required by your API (e.g. `"Bearer "` not just the raw JWT).
|
|
2814
|
-
|
|
2815
|
-
---
|
|
2816
|
-
|
|
2817
|
-
## License
|
|
2818
|
-
|
|
2819
|
-
This module is part of the shared-ui library.
|