@a.nemreen/dga-dynamic-form 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,15 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { InjectionToken, Type, OnInit, EnvironmentProviders } from '@angular/core';
3
- import { FormGroup, FormControl, FormBuilder, ValidatorFn, AbstractControl } from '@angular/forms';
3
+ import { FormGroup, FormControl, FormBuilder, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
4
4
  import { DgaStep, DgaSelectOption, DgaToastService } from '@a.nemreen/dga-ui';
5
5
  import { Observable } from 'rxjs';
6
6
  import { HttpClient } from '@angular/common/http';
7
7
 
8
- /** Bilingual label used across the schema. */
8
+ /** Localized label (en/ar required for schemas; extra locale keys optional). */
9
9
  interface DgaFormLabel {
10
10
  en: string;
11
11
  ar: string;
12
+ [locale: string]: string;
12
13
  }
13
14
  interface DgaFormFieldColumns {
14
15
  xl?: number;
@@ -24,7 +25,24 @@ interface DgaFormFieldOption {
24
25
  disabled?: boolean;
25
26
  [key: string]: unknown;
26
27
  }
27
- type DgaFormFieldType = 'text' | 'email' | 'textarea' | 'select' | 'multiselect' | 'checkbox' | 'radio' | 'phone' | 'file' | 'date' | 'chips' | 'toggle' | 'hidden' | 'number' | 'otp';
28
+ type DgaFormFieldType = 'text' | 'email' | 'textarea' | 'select' | 'multiselect' | 'checkbox' | 'radio' | 'phone' | 'file' | 'multifile' | 'multifilestaged' | 'date' | 'chips' | 'toggle' | 'hidden' | 'number' | 'otp' | 'password';
29
+ /** Pre-populated attachment from the server (edit mode). */
30
+ interface DgaExistingAttachment {
31
+ Id: number;
32
+ FileName: string;
33
+ ContentType: string;
34
+ }
35
+ type DgaStagedFileStatus = 'uploading' | 'success' | 'error';
36
+ /** Value shape for `multifilestaged` fields. */
37
+ interface DgaStagedFileRef {
38
+ fileId: string;
39
+ fileName: string;
40
+ sizeBytes?: number;
41
+ status?: DgaStagedFileStatus;
42
+ errorMessage?: string;
43
+ /** Client-only list identity when server reuses fileId. */
44
+ listKey?: string;
45
+ }
28
46
  type DgaInputRestriction = 'numbers' | 'english' | 'arabic';
29
47
  type DgaHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
30
48
  interface DgaFormField {
@@ -40,10 +58,22 @@ interface DgaFormField {
40
58
  hidden?: boolean;
41
59
  dependsOn?: string;
42
60
  showOnValues?: unknown[];
61
+ /** Show when selected option English label matches. */
62
+ showOnTitleEn?: string;
63
+ /** Show when selected option Arabic label matches. */
64
+ showOnTitleAr?: string;
65
+ /** Extra text field shown when selected option has `requiresTextInput`. */
66
+ extraField?: boolean;
67
+ /** Trigger field for `extraField` (defaults to `dependsOn`). */
68
+ extraFieldTrigger?: string;
43
69
  showWhenOptionProperty?: {
44
70
  property: string;
45
71
  value: unknown;
46
72
  };
73
+ /** CMS consent key — fetched from `consentBaseUrl` on the form config. */
74
+ consentServiceName?: string;
75
+ /** Keep select dropdown open (DGA select `openMode="always"`). */
76
+ alwaysOpen?: boolean;
47
77
  lookupDomain?: string;
48
78
  lookupName?: string;
49
79
  lookupFilter?: {
@@ -60,6 +90,11 @@ interface DgaFormField {
60
90
  acceptedFileTypes?: string;
61
91
  maxFileSize?: number;
62
92
  maxFiles?: number;
93
+ /** POST URL for `multifilestaged` staging uploads. Response: `{ fileId, fileName? }`. */
94
+ uploadEndpoint?: string;
95
+ /** DELETE/POST URL for staged file removal. Use `:id` or `:fileId` placeholder. */
96
+ deleteEndpoint?: string;
97
+ deleteMethod?: 'DELETE' | 'POST';
63
98
  rows?: number;
64
99
  minDate?: string;
65
100
  maxDate?: string;
@@ -94,7 +129,9 @@ interface DgaFormField {
94
129
  required?: boolean;
95
130
  min?: number;
96
131
  max?: number;
97
- errorMessages?: Partial<Record<'required' | 'pattern' | 'minLength' | 'maxLength' | 'email' | 'min' | 'max' | 'minDate' | 'maxDate' | 'maxChips' | 'invalidFormat', DgaFormLabel>>;
132
+ minDate?: string;
133
+ maxDate?: string;
134
+ errorMessages?: Partial<Record<'required' | 'pattern' | 'minLength' | 'maxLength' | 'email' | 'min' | 'max' | 'minDate' | 'maxDate' | 'maxChips' | 'invalidFormat' | 'invalidPrefix' | 'invalidLength' | 'passwordMismatch' | 'endDateAfterStartDate' | 'uploadInProgress' | 'maxFiles', DgaFormLabel>>;
98
135
  };
99
136
  }
100
137
  interface DgaFormStep {
@@ -103,6 +140,8 @@ interface DgaFormStep {
103
140
  description?: DgaFormLabel;
104
141
  fields: DgaFormField[];
105
142
  optional?: boolean;
143
+ /** Pre-fill values when entering this wizard step. */
144
+ initialData?: Record<string, unknown>;
106
145
  }
107
146
  interface DgaWizardConfig {
108
147
  enabled: boolean;
@@ -138,8 +177,13 @@ interface DgaDynamicFormConfig {
138
177
  idempotencyKey?: string;
139
178
  /** When true, HTTP submit is skipped and only `formSubmitted` is emitted. */
140
179
  emitOnly?: boolean;
180
+ /**
181
+ * Base URL for CMS consent fetch (`consentServiceName` fields).
182
+ * e.g. `https://cms.example.com/api` → GET `{base}/consents/{name}`.
183
+ */
184
+ consentBaseUrl?: string;
141
185
  }
142
- type DgaFormLocale = 'ar' | 'en';
186
+ type DgaFormLocale = string;
143
187
 
144
188
  interface DgaLookupRequest {
145
189
  domain: string;
@@ -173,11 +217,8 @@ interface DgaCaptchaAdapter {
173
217
  getToken(action?: string): Observable<string | null>;
174
218
  }
175
219
  interface DgaFormI18nAdapter {
176
- /** Resolve a bilingual label for the active locale. */
177
- resolveLabel(label: {
178
- en: string;
179
- ar: string;
180
- }, locale: DgaFormLocale): string;
220
+ /** Resolve a localized label for the active locale. */
221
+ resolveLabel(label: DgaFormLabel, locale: DgaFormLocale): string;
181
222
  /** Optional free-form key lookup (e.g. ngx-translate). */
182
223
  translate?(key: string, locale: DgaFormLocale): string;
183
224
  }
@@ -191,6 +232,10 @@ declare const DGA_LOOKUP_ADAPTER: InjectionToken<DgaLookupAdapter>;
191
232
  declare const DGA_SUBMIT_ADAPTER: InjectionToken<DgaSubmitAdapter>;
192
233
  declare const DGA_FORM_TOAST_ADAPTER: InjectionToken<DgaFormToastAdapter>;
193
234
  declare const DGA_CAPTCHA_ADAPTER: InjectionToken<DgaCaptchaAdapter>;
235
+ interface DgaHttpSecurityOptions {
236
+ allowedOrigins?: string[];
237
+ }
238
+ declare const DGA_HTTP_SECURITY: InjectionToken<DgaHttpSecurityOptions>;
194
239
  declare const DGA_FORM_I18N_ADAPTER: InjectionToken<DgaFormI18nAdapter>;
195
240
  declare const DGA_DYNAMIC_FORM_FIELD_REGISTRY: InjectionToken<DgaDynamicFieldRenderer[]>;
196
241
  /** Context passed into custom field renderer components (optional inject). */
@@ -221,13 +266,15 @@ declare class DgaDynamicFormFieldRegistry {
221
266
 
222
267
  declare class DgaDynamicForm {
223
268
  readonly config: _angular_core.InputSignal<DgaDynamicFormConfig>;
224
- readonly localeInput: _angular_core.InputSignal<DgaFormLocale | null>;
269
+ readonly localeInput: _angular_core.InputSignal<string | null>;
225
270
  readonly formSubmitted: _angular_core.OutputEmitterRef<unknown>;
226
271
  readonly formError: _angular_core.OutputEmitterRef<unknown>;
227
272
  readonly clearButtonClick: _angular_core.OutputEmitterRef<void>;
228
273
  readonly draftSaved: _angular_core.OutputEmitterRef<Record<string, unknown>>;
274
+ readonly fileDownload: _angular_core.OutputEmitterRef<DgaExistingAttachment>;
229
275
  private readonly fb;
230
276
  private readonly formService;
277
+ private readonly submitAdapter;
231
278
  private readonly toast;
232
279
  private readonly i18n;
233
280
  private readonly destroyRef;
@@ -239,7 +286,7 @@ declare class DgaDynamicForm {
239
286
  readonly submitting: _angular_core.WritableSignal<boolean>;
240
287
  readonly optionsMap: _angular_core.WritableSignal<Record<string, DgaFormFieldOption[]>>;
241
288
  private readonly formValues;
242
- readonly locale: _angular_core.Signal<DgaFormLocale>;
289
+ readonly locale: _angular_core.Signal<string>;
243
290
  readonly wizardEnabled: _angular_core.Signal<boolean>;
244
291
  readonly allFields: _angular_core.Signal<DgaFormField[]>;
245
292
  readonly currentStepFields: _angular_core.Signal<DgaFormField[]>;
@@ -247,10 +294,7 @@ declare class DgaDynamicForm {
247
294
  readonly stepperSteps: _angular_core.Signal<DgaStep[]>;
248
295
  readonly descriptionText: _angular_core.Signal<string>;
249
296
  constructor();
250
- labelOf(label: {
251
- en: string;
252
- ar: string;
253
- } | undefined): string;
297
+ labelOf(label: DgaFormLabel | undefined): string;
254
298
  submitLabel(): string;
255
299
  nextLabel(): string;
256
300
  previousLabel(): string;
@@ -271,6 +315,7 @@ declare class DgaDynamicForm {
271
315
  /** Keep stepper clicks aligned with Next validation / allowSkip rules. */
272
316
  readonly canActivateStep: (next: number, current: number) => boolean;
273
317
  nextStep(): void;
318
+ private skipEmptyStepsForward;
274
319
  onClear(): void;
275
320
  onSaveDraft(): void;
276
321
  onSubmit(): void;
@@ -283,21 +328,24 @@ declare class DgaDynamicForm {
283
328
  private visibleValid;
284
329
  private collectVisibleFieldNames;
285
330
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DgaDynamicForm, never>;
286
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DgaDynamicForm, "dga-dynamic-form", never, { "config": { "alias": "config"; "required": true; "isSignal": true; }; "localeInput": { "alias": "locale"; "required": false; "isSignal": true; }; }, { "formSubmitted": "formSubmitted"; "formError": "formError"; "clearButtonClick": "clearButtonClick"; "draftSaved": "draftSaved"; }, never, never, true, never>;
331
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DgaDynamicForm, "dga-dynamic-form", never, { "config": { "alias": "config"; "required": true; "isSignal": true; }; "localeInput": { "alias": "locale"; "required": false; "isSignal": true; }; }, { "formSubmitted": "formSubmitted"; "formError": "formError"; "clearButtonClick": "clearButtonClick"; "draftSaved": "draftSaved"; "fileDownload": "fileDownload"; }, never, never, true, never>;
287
332
  }
288
333
 
289
334
  declare class DgaDynamicFormField implements OnInit {
290
335
  readonly field: _angular_core.InputSignal<DgaFormField>;
291
336
  readonly form: _angular_core.InputSignal<FormGroup<any>>;
292
- readonly locale: _angular_core.InputSignal<DgaFormLocale>;
337
+ readonly locale: _angular_core.InputSignal<string>;
293
338
  readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
294
339
  readonly error: _angular_core.InputSignal<string>;
295
340
  readonly runtimeOptions: _angular_core.InputSignal<DgaFormFieldOption[]>;
341
+ readonly consentBaseUrl: _angular_core.InputSignal<string | undefined>;
296
342
  readonly optionsLoaded: _angular_core.OutputEmitterRef<{
297
343
  name: string;
298
344
  options: DgaFormFieldOption[];
299
345
  }>;
346
+ readonly fileDownload: _angular_core.OutputEmitterRef<DgaExistingAttachment>;
300
347
  private readonly lookup;
348
+ private readonly http;
301
349
  private readonly destroyRef;
302
350
  readonly loadedOptions: _angular_core.WritableSignal<DgaFormFieldOption[]>;
303
351
  readonly controlId: string;
@@ -308,14 +356,19 @@ declare class DgaDynamicFormField implements OnInit {
308
356
  readonly resolvedOptions: _angular_core.Signal<DgaFormFieldOption[]>;
309
357
  readonly selectOptions: _angular_core.Signal<DgaSelectOption[]>;
310
358
  ngOnInit(): void;
359
+ private mapExistingFileValue;
360
+ private fetchConsent;
361
+ private fetchLookup;
311
362
  optionLabel(opt: DgaFormFieldOption): string;
363
+ isPasswordField(): boolean;
312
364
  isChecked(value: unknown): boolean;
313
365
  toggleCheckbox(value: unknown, checked: boolean): void;
314
- onSelectSearch(_value: string | string[]): void;
366
+ onSelectSearchQuery(query: string): void;
367
+ onStagedFileDownload(event: DgaExistingAttachment): void;
315
368
  onRestrict(event: Event): void;
316
369
  private applyLookupFilter;
317
370
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DgaDynamicFormField, never>;
318
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DgaDynamicFormField, "dga-dynamic-form-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "form": { "alias": "form"; "required": true; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "runtimeOptions": { "alias": "runtimeOptions"; "required": false; "isSignal": true; }; }, { "optionsLoaded": "optionsLoaded"; }, never, never, true, never>;
371
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DgaDynamicFormField, "dga-dynamic-form-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "form": { "alias": "form"; "required": true; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "runtimeOptions": { "alias": "runtimeOptions"; "required": false; "isSignal": true; }; "consentBaseUrl": { "alias": "consentBaseUrl"; "required": false; "isSignal": true; }; }, { "optionsLoaded": "optionsLoaded"; "fileDownload": "fileDownload"; }, never, never, true, never>;
319
372
  }
320
373
 
321
374
  declare class DgaDynamicFormService {
@@ -354,25 +407,27 @@ declare function dgaFindSelectedOption(field: DgaFormField | undefined, value: u
354
407
  /**
355
408
  * Whether a field should be visible given the current form values.
356
409
  */
357
- declare function dgaIsFieldVisible(field: DgaFormField, values: Record<string, unknown>, getOptions?: (name: string) => DgaFormFieldOption[] | undefined): boolean;
358
- declare function dgaIsRequiredWhen(field: DgaFormField, values: Record<string, unknown>, getOptions?: (name: string) => DgaFormFieldOption[] | undefined): boolean;
410
+ declare function dgaIsFieldVisible(field: DgaFormField, values: Record<string, unknown>, getOptions?: (name: string) => DgaFormFieldOption[] | undefined, getField?: (name: string) => DgaFormField | undefined): boolean;
411
+ declare function dgaIsRequiredWhen(field: DgaFormField, values: Record<string, unknown>, getOptions?: (name: string) => DgaFormFieldOption[] | undefined, getField?: (name: string) => DgaFormField | undefined): boolean;
359
412
 
360
- /** Default bilingual label resolver. */
413
+ /** Known start/end date field pairs used by cross-field validators. */
414
+ declare const DGA_DATE_RANGE_PAIRS: ReadonlyArray<[string, string]>;
415
+ declare function dgaPasswordMatchValidator(passwordControl: AbstractControl): ValidatorFn;
416
+ /**
417
+ * Saudi mobile validation for type: phone fields.
418
+ * Accepts E.164 (+966…) or national digits (0xxxxxxxx or 5xxxxxxxx).
419
+ */
420
+ declare function dgaSaudiMobileValidator(control: AbstractControl): ValidationErrors | null;
421
+ /** Wire password match + date-range validators after form group is built. */
422
+ declare function dgaApplyCrossFieldValidators(form: FormGroup, fields: DgaFormField[]): void;
423
+
424
+ /** Default localized label resolver. */
361
425
  declare const defaultFormI18nAdapter: DgaFormI18nAdapter;
362
426
  /** No-op captcha (always null token). */
363
427
  declare const noopCaptchaAdapter: DgaCaptchaAdapter;
364
428
  /** Empty lookup (returns []). Override in the host app. */
365
429
  declare const emptyLookupAdapter: DgaLookupAdapter;
366
- interface DgaHttpAdapterSecurityOptions {
367
- /**
368
- * Allowed URL origins for schema-driven HTTP (e.g. `https://api.example.com`).
369
- * Absolute URLs outside the list are rejected.
370
- * Same-origin paths (`/api/...`) are always allowed.
371
- *
372
- * Strongly recommended for CMS / untrusted schemas. When `requireAllowedOrigins`
373
- * is true (see `provideDgaDynamicForm`), omitting this throws at bootstrap.
374
- */
375
- allowedOrigins?: string[];
430
+ interface DgaHttpAdapterSecurityOptions extends DgaHttpSecurityOptions {
376
431
  }
377
432
  /**
378
433
  * Validates schema-supplied URLs before HttpClient calls.
@@ -407,13 +462,11 @@ interface ProvideDgaDynamicFormOptions {
407
462
  */
408
463
  declare function provideDgaDynamicForm(options?: ProvideDgaDynamicFormOptions): EnvironmentProviders;
409
464
  /** Helper for templates / host apps. */
410
- declare function resolveFormLabel(label: {
411
- en: string;
412
- ar: string;
413
- } | undefined, locale: DgaFormLocale, fallback?: string): string;
465
+ declare function resolveFormLabel(label: DgaFormLabel | undefined, locale: DgaFormLocale, fallback?: string): string;
414
466
 
415
- declare function dgaResolveLabel(label: DgaFormLabel | undefined, locale: DgaFormLocale, fallback?: string): string;
467
+ type LocalizedMap = Partial<Record<string, string>>;
468
+ declare function dgaResolveLabel(label: LocalizedMap | DgaFormLabel | undefined, locale: DgaFormLocale, fallback?: string): string;
416
469
  declare function dgaDetectLocale(): DgaFormLocale;
417
470
 
418
- export { DGA_CAPTCHA_ADAPTER, DGA_DYNAMIC_FORM_FIELD_REGISTRY, DGA_FORM_I18N_ADAPTER, DGA_FORM_TOAST_ADAPTER, DGA_LOOKUP_ADAPTER, DGA_SUBMIT_ADAPTER, DgaDynamicForm, DgaDynamicFormField, DgaDynamicFormFieldRegistry, DgaDynamicFormService, createDgaToastAdapter, createHttpLookupAdapter, createHttpSubmitAdapter, defaultFormI18nAdapter, dgaAssertSafeHttpUrl, dgaBuildFormGroup, dgaBuildValidators, dgaCollectFields, dgaCreateControl, dgaDefaultValueForField, dgaDetectLocale, dgaFindSelectedOption, dgaIsFieldVisible, dgaIsRequiredWhen, dgaResolveLabel, dgaSetControlValidators, emptyLookupAdapter, noopCaptchaAdapter, provideDgaDynamicForm, resolveFormLabel };
419
- export type { DgaCaptchaAdapter, DgaDynamicFieldHostContext, DgaDynamicFieldRenderer, DgaDynamicFormConfig, DgaDynamicFormPayloadTransformer, DgaFormField, DgaFormFieldColumns, DgaFormFieldOption, DgaFormFieldType, DgaFormI18nAdapter, DgaFormLabel, DgaFormLocale, DgaFormStep, DgaFormToastAdapter, DgaHttpAdapterSecurityOptions, DgaHttpMethod, DgaInputRestriction, DgaLookupAdapter, DgaLookupRequest, DgaSubmitAdapter, DgaSubmitRequest, DgaToastVariant, DgaWizardConfig, ProvideDgaDynamicFormOptions };
471
+ export { DGA_CAPTCHA_ADAPTER, DGA_DATE_RANGE_PAIRS, DGA_DYNAMIC_FORM_FIELD_REGISTRY, DGA_FORM_I18N_ADAPTER, DGA_FORM_TOAST_ADAPTER, DGA_HTTP_SECURITY, DGA_LOOKUP_ADAPTER, DGA_SUBMIT_ADAPTER, DgaDynamicForm, DgaDynamicFormField, DgaDynamicFormFieldRegistry, DgaDynamicFormService, createDgaToastAdapter, createHttpLookupAdapter, createHttpSubmitAdapter, defaultFormI18nAdapter, dgaApplyCrossFieldValidators, dgaAssertSafeHttpUrl, dgaBuildFormGroup, dgaBuildValidators, dgaCollectFields, dgaCreateControl, dgaDefaultValueForField, dgaDetectLocale, dgaFindSelectedOption, dgaIsFieldVisible, dgaIsRequiredWhen, dgaPasswordMatchValidator, dgaResolveLabel, dgaSaudiMobileValidator, dgaSetControlValidators, emptyLookupAdapter, noopCaptchaAdapter, provideDgaDynamicForm, resolveFormLabel };
472
+ export type { DgaCaptchaAdapter, DgaDynamicFieldHostContext, DgaDynamicFieldRenderer, DgaDynamicFormConfig, DgaDynamicFormPayloadTransformer, DgaExistingAttachment, DgaFormField, DgaFormFieldColumns, DgaFormFieldOption, DgaFormFieldType, DgaFormI18nAdapter, DgaFormLabel, DgaFormLocale, DgaFormStep, DgaFormToastAdapter, DgaHttpAdapterSecurityOptions, DgaHttpMethod, DgaHttpSecurityOptions, DgaInputRestriction, DgaLookupAdapter, DgaLookupRequest, DgaStagedFileRef, DgaSubmitAdapter, DgaSubmitRequest, DgaToastVariant, DgaWizardConfig, ProvideDgaDynamicFormOptions };