@a.nemreen/dga-dynamic-form 0.1.0 → 0.1.1
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/README.md +197 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,8 +14,6 @@ npm install @a.nemreen/dga-dynamic-form @a.nemreen/dga-ui @a.nemreen/dga-tokens
|
|
|
14
14
|
|
|
15
15
|
## Setup
|
|
16
16
|
|
|
17
|
-
Provide adapters once in `app.config.ts`:
|
|
18
|
-
|
|
19
17
|
```ts
|
|
20
18
|
import { provideHttpClient } from '@angular/common/http';
|
|
21
19
|
import { provideDgaDynamicForm } from '@a.nemreen/dga-dynamic-form';
|
|
@@ -23,25 +21,24 @@ import { provideDgaDynamicForm } from '@a.nemreen/dga-dynamic-form';
|
|
|
23
21
|
export const appConfig = {
|
|
24
22
|
providers: [
|
|
25
23
|
provideHttpClient(),
|
|
26
|
-
provideDgaDynamicForm({ httpSubmit: true, dgaToast: true }),
|
|
24
|
+
provideDgaDynamicForm({ httpSubmit: true, httpLookup: true, dgaToast: true }),
|
|
27
25
|
],
|
|
28
26
|
};
|
|
29
27
|
```
|
|
30
28
|
|
|
31
|
-
Ensure Tailwind scans the package:
|
|
32
|
-
|
|
33
29
|
```css
|
|
34
30
|
@source "../node_modules/@a.nemreen/dga-dynamic-form/**/*.{mjs,js}";
|
|
35
31
|
@source "../node_modules/@a.nemreen/dga-ui/**/*.{mjs,js}";
|
|
36
32
|
```
|
|
37
33
|
|
|
38
|
-
##
|
|
34
|
+
## Quick usage
|
|
39
35
|
|
|
40
36
|
```ts
|
|
41
37
|
import { DgaDynamicForm, type DgaDynamicFormConfig } from '@a.nemreen/dga-dynamic-form';
|
|
42
38
|
|
|
43
39
|
readonly config: DgaDynamicFormConfig = {
|
|
44
|
-
|
|
40
|
+
endpoint: 'https://api.example.com/contact',
|
|
41
|
+
method: 'POST',
|
|
45
42
|
submitButtonLabel: { en: 'Send', ar: 'إرسال' },
|
|
46
43
|
fields: [
|
|
47
44
|
{
|
|
@@ -51,13 +48,6 @@ readonly config: DgaDynamicFormConfig = {
|
|
|
51
48
|
required: true,
|
|
52
49
|
columns: { md: 6 },
|
|
53
50
|
},
|
|
54
|
-
{
|
|
55
|
-
name: 'email',
|
|
56
|
-
type: 'email',
|
|
57
|
-
label: { en: 'Email', ar: 'البريد' },
|
|
58
|
-
required: true,
|
|
59
|
-
columns: { md: 6 },
|
|
60
|
-
},
|
|
61
51
|
],
|
|
62
52
|
};
|
|
63
53
|
```
|
|
@@ -66,20 +56,204 @@ readonly config: DgaDynamicFormConfig = {
|
|
|
66
56
|
<dga-dynamic-form [config]="config" (formSubmitted)="onSubmit($event)" />
|
|
67
57
|
```
|
|
68
58
|
|
|
69
|
-
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Config reference (`DgaDynamicFormConfig`)
|
|
62
|
+
|
|
63
|
+
Passed as `[config]` on `<dga-dynamic-form>`.
|
|
64
|
+
|
|
65
|
+
| Key | Type | Default | Description |
|
|
66
|
+
|-----|------|---------|-------------|
|
|
67
|
+
| `endpoint` | `string?` | — | Submit URL. Omit (or use `emitOnly`) to only emit `formSubmitted`. |
|
|
68
|
+
| `method` | `'GET' \| 'POST' \| 'PUT' \| 'PATCH' \| 'DELETE'` | `'POST'` | HTTP method for the submit adapter. |
|
|
69
|
+
| `emitOnly` | `boolean?` | `false` | Skip HTTP; emit the payload on `formSubmitted`. |
|
|
70
|
+
| `fields` | `DgaFormField[]` | **required** | Field list. Can be `[]` when using `wizard.steps` only. |
|
|
71
|
+
| `description` | `DgaFormLabel?` | — | Bilingual text above the form. |
|
|
72
|
+
| `submitButtonLabel` | `DgaFormLabel?` | Submit / إرسال | Primary button label. |
|
|
73
|
+
| `clearButtonLabel` | `DgaFormLabel?` | — | Shows Clear when set. |
|
|
74
|
+
| `saveButtonLabel` | `DgaFormLabel?` | — | Draft save button label. |
|
|
75
|
+
| `successMessage` | `DgaFormLabel?` | — | Toast on success. |
|
|
76
|
+
| `errorMessage` | `DgaFormLabel?` | — | Toast on failure. |
|
|
77
|
+
| `fieldMapping` | `Record<string, string>?` | — | Rename field keys in the submit payload. |
|
|
78
|
+
| `payloadTransformer` | `(data) => object \| FormData` | — | Final reshape before submit. |
|
|
79
|
+
| `formId` | `string?` | — | localStorage namespace for drafts. |
|
|
80
|
+
| `enableLocalStorageSave` | `boolean?` | `false` | Enable Save draft + restore on init. |
|
|
81
|
+
| `idempotencyKey` | `string?` | — | Sent as `Idempotency-Key` header. |
|
|
82
|
+
| `wizard` | `DgaWizardConfig?` | — | Multi-step mode (see below). |
|
|
83
|
+
|
|
84
|
+
`DgaFormLabel` is always `{ en: string; ar: string }`.
|
|
85
|
+
|
|
86
|
+
### Example with endpoint
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const EDIT_STUDY: DgaDynamicFormConfig = {
|
|
90
|
+
endpoint: `${apiUrl}/consulting-studies/{id}`,
|
|
91
|
+
method: 'PUT',
|
|
92
|
+
successMessage: { en: 'Saved', ar: 'تم الحفظ' },
|
|
93
|
+
errorMessage: { en: 'Failed', ar: 'فشل' },
|
|
94
|
+
fields: [ /* ... */ ],
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Wizard (`config.wizard` → `DgaWizardConfig`)
|
|
101
|
+
|
|
102
|
+
When `wizard.enabled` is true, fields are taken from `wizard.steps[].fields` (root `fields` can be empty).
|
|
103
|
+
|
|
104
|
+
| Key | Type | Default | Description |
|
|
105
|
+
|-----|------|---------|-------------|
|
|
106
|
+
| `enabled` | `boolean` | — | Turn on stepper UI. |
|
|
107
|
+
| `steps` | `DgaFormStep[]` | — | Ordered steps. |
|
|
108
|
+
| `validateOnStepChange` | `boolean?` | `true` | Block Next if current step invalid. |
|
|
109
|
+
| `allowSkipSteps` | `boolean?` | `false` | Allow jumping ahead (reserved). |
|
|
110
|
+
| `showSaveButton` | `boolean?` | — | Show draft save in wizard chrome. |
|
|
111
|
+
| `autoSave` | `boolean?` | — | Auto-save drafts (host / reserved). |
|
|
112
|
+
| `saveEndpoint` | `string?` | — | Optional draft HTTP URL. |
|
|
113
|
+
| `saveMethod` | `DgaHttpMethod?` | — | Method for `saveEndpoint`. |
|
|
114
|
+
| `nextButtonText` | `DgaFormLabel?` | Next / التالي | |
|
|
115
|
+
| `previousButtonText` | `DgaFormLabel?` | Previous / السابق | |
|
|
116
|
+
| `submitButtonText` | `DgaFormLabel?` | Finish / إنهاء | Final step. |
|
|
117
|
+
| `saveButtonText` | `DgaFormLabel?` | Save / حفظ | |
|
|
118
|
+
|
|
119
|
+
### Step (`DgaFormStep`)
|
|
70
120
|
|
|
71
|
-
|
|
121
|
+
| Key | Type | Description |
|
|
122
|
+
|-----|------|-------------|
|
|
123
|
+
| `id` | `string` | Stable step id. |
|
|
124
|
+
| `title` | `DgaFormLabel` | Stepper title. |
|
|
125
|
+
| `description` | `DgaFormLabel?` | Stepper subtitle. |
|
|
126
|
+
| `fields` | `DgaFormField[]` | Fields in this step. |
|
|
127
|
+
| `optional` | `boolean?` | Mark step optional. |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Field (`DgaFormField`)
|
|
132
|
+
|
|
133
|
+
### Core
|
|
134
|
+
|
|
135
|
+
| Key | Type | Description |
|
|
136
|
+
|-----|------|-------------|
|
|
137
|
+
| `name` | `string` | FormControl name / payload key. |
|
|
138
|
+
| `type` | `DgaFormFieldType` | See built-in types below. |
|
|
139
|
+
| `label` | `DgaFormLabel` | Bilingual label. |
|
|
140
|
+
| `required` | `boolean?` | Always-required. |
|
|
141
|
+
| `placeholder` | `DgaFormLabel?` | |
|
|
142
|
+
| `hint` | `DgaFormLabel?` | Helper under the field. |
|
|
143
|
+
| `value` | `unknown?` | Initial value. |
|
|
144
|
+
| `disabled` | `boolean?` | |
|
|
145
|
+
| `readonly` | `boolean?` | Text-like inputs. |
|
|
146
|
+
| `hidden` | `boolean?` | Hide from layout. |
|
|
147
|
+
| `excludeFromPayload` | `boolean?` | Keep in UI; drop from submit body. |
|
|
148
|
+
| `columns` | `{ sm?, md?, lg?, xl? }` | 12-column grid spans (default 12). |
|
|
149
|
+
| `maxLength` | `number?` | |
|
|
150
|
+
| `rows` | `number?` | Textarea rows. |
|
|
151
|
+
| `inputRestriction` | `'numbers' \| 'english' \| 'arabic'` | Filter keystrokes. |
|
|
152
|
+
|
|
153
|
+
### Built-in `type` values
|
|
154
|
+
|
|
155
|
+
`text` · `email` · `textarea` · `number` · `otp` · `phone` · `select` · `multiselect` · `checkbox` · `radio` · `date` · `chips` · `toggle` · `file` · `hidden`
|
|
156
|
+
|
|
157
|
+
### Options (select / radio / checkbox)
|
|
158
|
+
|
|
159
|
+
| Key | Type | Description |
|
|
160
|
+
|-----|------|-------------|
|
|
161
|
+
| `options` | `DgaFormFieldOption[]?` | Static options. |
|
|
162
|
+
| `groupedOptions` | `{ groupLabel, options }[]?` | Grouped static options. |
|
|
163
|
+
| `optionsLayout` | `'stack' \| 'grid'` | Radio/checkbox layout (default `stack`). |
|
|
164
|
+
|
|
165
|
+
**Option object:** `value`, `label`, optional `requiresTextInput`, `description`, `disabled`.
|
|
166
|
+
|
|
167
|
+
### Visibility & conditional required
|
|
168
|
+
|
|
169
|
+
| Key | Type | Description |
|
|
170
|
+
|-----|------|-------------|
|
|
171
|
+
| `dependsOn` | `string?` | Other field that controls visibility. |
|
|
172
|
+
| `showOnValues` | `unknown[]?` | Show when `dependsOn` value is in list. |
|
|
173
|
+
| `showWhenOptionProperty` | `{ property, value }?` | Show when selected option has `property === value`. |
|
|
174
|
+
| `requiredWhen` | object? | Conditional required (see below). |
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
requiredWhen: {
|
|
178
|
+
field: 'topic',
|
|
179
|
+
value?: 'other', // single match
|
|
180
|
+
values?: ['a', 'b'], // any-of match
|
|
181
|
+
optionProperty?: 'requiresTextInput',
|
|
182
|
+
optionPropertyValue?: true,
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Lookup (remote options)
|
|
187
|
+
|
|
188
|
+
| Key | Type | Description |
|
|
189
|
+
|-----|------|-------------|
|
|
190
|
+
| `lookupDomain` | `string?` | Base URL for lookup adapter. |
|
|
191
|
+
| `lookupName` | `string?` | Resource → `{domain}/lookup/{name}`. |
|
|
192
|
+
| `lookupFilter` | `{ property, value }?` | Client-side filter on loaded options. |
|
|
193
|
+
| `useParentId` | `boolean?` | Pass `parentId` for cascading lookups. |
|
|
194
|
+
| `searchLookup` | `boolean?` | Search-as-you-type. |
|
|
195
|
+
| `searchParamName` | `string?` | Query param (default `q`). |
|
|
196
|
+
| `minSearchLength` | `number?` | Min chars before search (default 3). |
|
|
197
|
+
|
|
198
|
+
### Type-specific
|
|
199
|
+
|
|
200
|
+
| Key | Types | Description |
|
|
201
|
+
|-----|-------|-------------|
|
|
202
|
+
| `minDate` / `maxDate` | `date` | ISO date bounds. |
|
|
203
|
+
| `monthPicker` | `date` | Month-only picker. |
|
|
204
|
+
| `acceptedFileTypes` | `file` | e.g. `.pdf,.png`. |
|
|
205
|
+
| `maxFileSize` | `file` | Max bytes. |
|
|
206
|
+
| `maxFiles` | `file` | Max files (`>1` → multiple). |
|
|
207
|
+
| `maxChips` | `chips` | Max chip count. |
|
|
208
|
+
| `allowDuplicates` | `chips` | Allow duplicate chips. |
|
|
209
|
+
|
|
210
|
+
### Validation (`field.validation`)
|
|
211
|
+
|
|
212
|
+
| Key | Description |
|
|
213
|
+
|-----|-------------|
|
|
214
|
+
| `pattern` | Regex string, or `'email'`. |
|
|
215
|
+
| `minLength` / `maxLength` | Length validators. |
|
|
216
|
+
| `min` / `max` | Numeric bounds. |
|
|
217
|
+
| `required` | Same as `field.required`. |
|
|
218
|
+
| `errorMessages.*` | Bilingual messages for `required`, `pattern`, `minLength`, `maxLength`, `email`, `min`, `max`, `minDate`, `maxDate`, `maxChips`, `invalidFormat`. |
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Component API
|
|
223
|
+
|
|
224
|
+
| Member | Kind | Description |
|
|
225
|
+
|--------|------|-------------|
|
|
226
|
+
| `config` | input | `DgaDynamicFormConfig` (required). |
|
|
227
|
+
| `locale` | input | `'ar' \| 'en' \| null` (default from `<html lang>`). |
|
|
228
|
+
| `formSubmitted` | output | HTTP response, or payload when `emitOnly` / no endpoint. |
|
|
229
|
+
| `formError` | output | Submit failure. |
|
|
230
|
+
| `clearButtonClick` | output | After Clear. |
|
|
231
|
+
| `draftSaved` | output | After localStorage save. |
|
|
232
|
+
| `patchFormValues(values)` | method | Patch controls. |
|
|
233
|
+
| `getForm()` | method | Returns `FormGroup`. |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Adapters
|
|
72
238
|
|
|
73
239
|
| Token | Role |
|
|
74
240
|
|-------|------|
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `DGA_FORM_TOAST_ADAPTER` |
|
|
78
|
-
| `DGA_CAPTCHA_ADAPTER` |
|
|
79
|
-
| `DGA_FORM_I18N_ADAPTER` |
|
|
80
|
-
| `DGA_DYNAMIC_FORM_FIELD_REGISTRY` |
|
|
241
|
+
| `DGA_SUBMIT_ADAPTER` | `submit({ endpoint, method, body, headers })` |
|
|
242
|
+
| `DGA_LOOKUP_ADAPTER` | `lookup({ domain, name, parentId?, search? })` |
|
|
243
|
+
| `DGA_FORM_TOAST_ADAPTER` | `show({ title?, message?, variant? })` |
|
|
244
|
+
| `DGA_CAPTCHA_ADAPTER` | `getToken(action?) → Observable<string \| null>` |
|
|
245
|
+
| `DGA_FORM_I18N_ADAPTER` | `resolveLabel(label, locale)` |
|
|
246
|
+
| `DGA_DYNAMIC_FORM_FIELD_REGISTRY` | `{ type, component }[]` custom field renderers |
|
|
81
247
|
|
|
82
|
-
|
|
248
|
+
```ts
|
|
249
|
+
provideDgaDynamicForm({
|
|
250
|
+
httpSubmit: true,
|
|
251
|
+
httpLookup: true,
|
|
252
|
+
dgaToast: true,
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Custom field type
|
|
83
257
|
|
|
84
258
|
```ts
|
|
85
259
|
{
|
|
@@ -87,7 +261,3 @@ Override via InjectionTokens:
|
|
|
87
261
|
useValue: [{ type: 'nationalId', component: NationalIdField }],
|
|
88
262
|
}
|
|
89
263
|
```
|
|
90
|
-
|
|
91
|
-
## Built-in field types
|
|
92
|
-
|
|
93
|
-
`text` · `email` · `textarea` · `number` · `otp` · `phone` · `select` · `multiselect` · `checkbox` · `radio` · `date` · `chips` · `toggle` · `file` · `hidden`
|