@a.nemreen/dga-dynamic-form 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Schema-driven dynamic forms for Angular, rendered with [`@a.nemreen/dga-ui`](https://www.npmjs.com/package/@a.nemreen/dga-ui) controls.
4
4
 
5
- **Version 0.1.8** — password match, staged uploads, lookups, wizard polish, and cross-field validators.
5
+ **Version 0.2.0** — wizard card chrome, Back / Save as draft actions, and selectable option cards.
6
6
 
7
7
  ## Install
8
8
 
@@ -176,12 +176,14 @@ When both `password` and `confirmPassword` fields exist, `passwordMismatch` vali
176
176
  | `consentBaseUrl` | `string?` | — | CMS base for `consentServiceName` fields → GET `{base}/consents/{name}`. |
177
177
  | `wizard` | `DgaWizardConfig?` | — | Multi-step mode (see below). |
178
178
 
179
- `DgaFormLabel` is always `{ en: string; ar: string }`.
179
+ `DgaFormLabel` is a map of locale code → string. `en` and `ar` are required; extra keys match `provideDgaLocales` (e.g. `{ en, ar, ur }`). Resolution: exact locale → `en` → `ar` first non-empty value.
180
180
 
181
181
  ---
182
182
 
183
183
  ## Wizard (`config.wizard`)
184
184
 
185
+ When `enabled`, the form renders DGA chrome: a numbered stepper with a compact radial step counter, the current step in a card (title, description, optional badge), **Back** (outline, start), **Save as draft** (ghost), and **Next** / **Submit** (primary, end). Reloading restores a local draft behind an info banner when `enableLocalStorageSave` is on.
186
+
185
187
  | Key | Type | Default | Description |
186
188
  |-----|------|---------|-------------|
187
189
  | `enabled` | `boolean` | — | Turn on stepper UI. |
@@ -262,7 +264,9 @@ HTTP adapter maps `Id/Title`, `Code/Name`, and `{UserName,Email,DisplayName}` sh
262
264
  | `monthPicker` | date | Month-only mode. |
263
265
  | `maxChips` | chips | Max chip count validator. |
264
266
  | `allowDuplicates` | chips | Allow duplicate chip values. |
267
+ | `countries` | phone | Extra dial codes after KSA (`+966`). `{ code, label?, name?: { en, ar } }`. Duplicate codes are skipped. Saudi mobile rules still apply to `+966`; other codes only require digits. |
265
268
  | `alwaysOpen` | select | `openMode="always"` on `dga-select`. |
269
+ | `optionsLayout` | radio, checkbox | `'stack'` (default), `'grid'`, or `'cards'` (selectable DGA option cards). |
266
270
  | `consentServiceName` | checkbox | Fetch label from CMS (`consentBaseUrl`). |
267
271
  | `excludeFromDraft` / `sensitive` | any | Strip from localStorage drafts. |
268
272
 
@@ -306,7 +310,7 @@ Supports `pattern`, `minLength`, `maxLength`, `min`, `max`, `minDate`, `maxDate`
306
310
 
307
311
  ### Exported utilities
308
312
 
309
- `dgaBuildFormGroup`, `dgaIsFieldVisible`, `dgaIsRequiredWhen`, `dgaApplyCrossFieldValidators`, `dgaSaudiMobileValidator`, `dgaPasswordMatchValidator`, `dgaAssertSafeHttpUrl`, `createHttpLookupAdapter`, `createHttpSubmitAdapter`.
313
+ `dgaBuildFormGroup`, `dgaIsFieldVisible`, `dgaIsRequiredWhen`, `dgaApplyCrossFieldValidators`, `dgaSaudiMobileValidator`, `dgaPhoneFieldValidator`, `dgaPasswordMatchValidator`, `dgaAssertSafeHttpUrl`, `createHttpLookupAdapter`, `createHttpSubmitAdapter`.
310
314
 
311
315
  ## License
312
316
 
@@ -1,4 +1,4 @@
1
- import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectable as Y,isDevMode as ze,makeEnvironmentProviders as Be,input as u,booleanAttribute as J,signal as m,computed as p,output as g,ChangeDetectionStrategy as V,Component as R,DestroyRef as Q,effect as k,untracked as q}from"@angular/core";import{NgComponentOutlet as X}from"@angular/common";import*as h from"@angular/forms";import{Validators as b,ReactiveFormsModule as O,FormsModule as Z,FormBuilder as _e}from"@angular/forms";import{switchMap as ee,catchError as $e,map as Ue,startWith as Ge}from"rxjs/operators";import{takeUntilDestroyed as T}from"@angular/core/rxjs-interop";import{DgaToastService as je,DgaButton as A,DgaIcon as te,dgaProvideValueAccessor as ae,DgaField as ie,DgaInput as ne,DgaSelect as oe,DgaCheckbox as re,DgaRadio as le,DgaPhoneInput as se,DgaChipInput as de,DgaSwitch as ue,DgaUpload as ce,DgaNumberInput as pe,DgaOtp as fe,DgaStepper as me}from"@a.nemreen/dga-ui";import{of as x,throwError as ge,startWith as We,catchError as Ke}from"rxjs";import{HttpClient as S,HttpHeaders as He}from"@angular/common/http";import{DgaDatePicker as he}from"@a.nemreen/dga-ui/date-picker";const z=new y("DGA_LOOKUP_ADAPTER"),E=new y("DGA_SUBMIT_ADAPTER"),B=new y("DGA_FORM_TOAST_ADAPTER"),_=new y("DGA_CAPTCHA_ADAPTER"),$=new y("DGA_HTTP_SECURITY"),U=new y("DGA_FORM_I18N_ADAPTER"),be=new y("DGA_DYNAMIC_FORM_FIELD_REGISTRY"),ve=[["StartDate","EndDate"],["UseCaseStartDate","UseCaseEndDate"],["startDate","endDate"]];function ye(i){return e=>e.value&&i.value!==e.value?{passwordMismatch:!0}:null}function Ye(i){return e=>{if(!e.value||!i.value)return null;const t=new Date(e.value),a=new Date(i.value),n=new Date(a);return n.setDate(n.getDate()+1),t>=n?null:{endDateAfterStartDate:!0}}}function xe(i){const e=(i.value??"").toString().trim();if(!e)return null;let t=e;return e.startsWith("+966")?t=e.slice(4):e.startsWith("966")&&(t=e.slice(3)),/^\d+$/.test(t)?t.startsWith("0")?t.length===10?null:{invalidLength:!0}:t.startsWith("5")?t.length===9?null:{invalidLength:!0}:{invalidPrefix:!0}:{invalidFormat:!0}}function Je(i){return e=>{if(!e.value)return null;const t=new Date(e.value),a=new Date(i);return t>=a?null:{minDate:!0}}}function Qe(i){return e=>{if(!e.value)return null;const t=new Date(e.value),a=new Date(i);return t<=a?null:{maxDate:!0}}}function Xe(i){const e=i.value;return Array.isArray(e)&&e.some(a=>a&&typeof a=="object"&&a.status==="uploading")?{uploadInProgress:!0}:null}function De(i,e){const t=i.get("password"),a=i.get("confirmPassword");t&&a&&(a.addValidators(ye(t)),a.updateValueAndValidity({emitEvent:!1}));for(const[n,o]of ve){const r=i.get(n),s=i.get(o);if(!r||!s)continue;s.addValidators(Ye(r));const d=e.find(w=>w.name===o),f=w=>{if(d?.validation){if(w){const qe=new Date(w),P=new Date(qe);P.setDate(P.getDate()+1),d.validation.minDate=P.toISOString().split("T")[0]}else delete d.validation.minDate;s.updateValueAndValidity({emitEvent:!1})}};f(r.value),r.valueChanges.subscribe(w=>f(w))}}const we=["checkbox","multiselect","chips","file","multifile","multifilestaged"],Ze=["toggle"];function Se(i){return i.value!==void 0?i.value:we.includes(i.type)?[]:Ze.includes(i.type)?!1:""}function G(i,e){const t=[],a=i.validation;(e||i.required||a?.required)&&(we.includes(i.type)?t.push(et):i.type==="toggle"?t.push(b.requiredTrue):t.push(b.required)),(i.type==="email"||a?.pattern==="email")&&t.push(b.email);const n=a?.minLength;n!=null&&t.push(b.minLength(n));const o=a?.maxLength??i.maxLength;if(o!=null&&t.push(b.maxLength(o)),a?.pattern&&a.pattern!=="email"){const r=a.pattern;if(typeof r=="string"&&r.length>0&&r.length<=200)try{t.push(b.pattern(r))}catch{}}return a?.min!=null&&t.push(b.min(a.min)),a?.max!=null&&t.push(b.max(a.max)),i.maxChips!=null&&i.type==="chips"&&t.push(tt(i.maxChips)),i.type==="phone"&&t.push(xe),i.type==="multifilestaged"&&t.push(Xe),a?.minDate&&t.push(Je(a.minDate)),a?.maxDate&&t.push(Qe(a.maxDate)),t}function et(i){const e=i.value;return Array.isArray(e)&&e.length>0?null:{required:!0}}function tt(i){return e=>{const t=e.value;return Array.isArray(t)&&t.length>i?{maxChips:{max:i,actual:t.length}}:null}}function Ie(i,e,t=!0){const a=!!e.disabled;return i.control({value:Se(e),disabled:a},{validators:G(e,t&&!e.requiredWhen)})}function Ce(i,e){const t={};for(const a of e){if(a.hidden&&a.type==="hidden"){t[a.name]=i.control(a.value??"");continue}t[a.name]=Ie(i,a)}return i.group(t)}function Le(i,e){return e?.length?e.flatMap(t=>t.fields):i}function Fe(i,e,t){i.setValidators(G(e,t)),i.updateValueAndValidity({emitEvent:!1})}function j(i,e){return Array.isArray(i)?i.some(t=>t==e):i==e}function at(i){return i.label?.en??""}function it(i){return i.label?.ar??""}function M(i,e,t,a){return ke(a?.(i)??{name:i,type:"select",label:{en:"",ar:""}},e,t?.(i))}function nt(i,e,t,a,n){if(!i.showOnTitleEn&&!i.showOnTitleAr||t==null||t==="")return!1;const o=M(e,t,a,n);if(!o)return!1;const r=i.showOnTitleEn?at(o)===i.showOnTitleEn:!0,s=i.showOnTitleAr?it(o)===i.showOnTitleAr:!0;return r&&s}function Ne(i,e,t,a){if(!i.extraField)return!0;const n=i.extraFieldTrigger||i.dependsOn;if(!n)return!1;const o=e[n];return M(n,o,t,a)?.requiresTextInput===!0}function ke(i,e,t){return i?[...t??[],...i.options??[],...i.groupedOptions?.flatMap(n=>n.options)??[]].find(n=>n.value==e):void 0}function W(i,e,t,a){if(i.hidden&&i.type!=="hidden"||i.type==="hidden")return!1;if(!i.dependsOn&&!i.extraField)return!0;const n=i.dependsOn??i.extraFieldTrigger;if(!n)return Ne(i,e,t,a);const o=e[n];if(i.showOnValues?.length&&!i.showOnValues.some(r=>j(o,r))||(i.showOnTitleEn||i.showOnTitleAr)&&!nt(i,n,o,t,a))return!1;if(i.showWhenOptionProperty){const r=M(n,o,t,a),s=i.showWhenOptionProperty.property;if(!r||r[s]!==i.showWhenOptionProperty.value)return!1}return!(i.extraField&&!Ne(i,e,t,a)||i.dependsOn&&!i.showOnValues?.length&&!i.showWhenOptionProperty&&!i.showOnTitleEn&&!i.showOnTitleAr&&!i.extraField&&(o==null||o===""||Array.isArray(o)&&o.length===0))}function K(i,e,t,a){const n=i.requiredWhen;if(!n)return!!i.required;const o=e[n.field];if(n.optionProperty!=null){const r=M(n.field,o,t,a);return!!r&&r[n.optionProperty]===n.optionPropertyValue}return n.values?.length?n.values.some(r=>j(o,r)):n.value!==void 0?j(o,n.value):o!=null&&o!==""}const ot=new Set(["otp","file"]),Oe=/password|passwd|secret|token|otp|national.?id|ssn|pin$/i;class C{submitAdapter=c(E,{optional:!0});captcha=c(_,{optional:!0});buildPayload(e,t,a){const n=new Set(a.filter(r=>r.excludeFromPayload).map(r=>r.name));let o={};for(const[r,s]of Object.entries(t)){if(n.has(r))continue;const d=e.fieldMapping?.[r]??r;o[d]=s}return e.payloadTransformer?e.payloadTransformer(o):o}hasFileValues(e){return Object.values(e).some(t=>t instanceof File||Array.isArray(t)&&t.some(a=>a instanceof File||a?.file instanceof File))}toFormData(e){const t=new FormData;for(const[a,n]of Object.entries(e))if(n!=null)if(n instanceof File)t.append(a,n);else if(Array.isArray(n))for(const o of n)o instanceof File?t.append(a,o):o?.file instanceof File?t.append(a,o.file,o.name):t.append(a,typeof o=="string"?o:JSON.stringify(o));else typeof n=="object"?t.append(a,JSON.stringify(n)):t.append(a,String(n));return t}submitForm(e,t,a){if(!e.endpoint||!this.submitAdapter)return x(t);const n=this.buildPayload(e,t,a),o=n instanceof FormData?n:this.hasFileValues(n)?this.toFormData(n):n,r={};return e.idempotencyKey&&(r["Idempotency-Key"]=e.idempotencyKey),(this.captcha?.getToken("submit")??x(null)).pipe(ee(d=>(d&&(r["X-Recaptcha-Token"]=d),x({endpoint:e.endpoint,method:e.method??"POST",body:o,headers:r})))).pipe(ee(d=>this.submitAdapter.submit(d)),$e(d=>{throw d}))}saveDraft(e,t,a=[]){if(typeof localStorage>"u")return;const n=new Map(a.map(r=>[r.name,r])),o={};for(const[r,s]of Object.entries(t)){const d=n.get(r);d&&this.isDraftBlocked(d)||!d&&Oe.test(r)||s instanceof File||(o[r]=s)}localStorage.setItem(this.storageKey(e),JSON.stringify(o))}loadDraft(e){if(typeof localStorage>"u")return null;const t=localStorage.getItem(this.storageKey(e));if(!t)return null;try{return JSON.parse(t)}catch{return null}}clearDraft(e){typeof localStorage>"u"||localStorage.removeItem(this.storageKey(e))}isDraftBlocked(e){return e.excludeFromDraft||e.sensitive||e.excludeFromPayload||ot.has(e.type)?!0:Oe.test(e.name)}storageKey(e){return`dga-dynamic-form:${e}`}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:C,deps:[],target:l.ɵɵFactoryTarget.Injectable});static ɵprov=l.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:C,providedIn:"root"})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:C,decorators:[{type:Y,args:[{providedIn:"root"}]}]});function I(i,e,t=""){return i?e==="en"?i.en||i.ar:i.ar||i.en:t}function Te(){return typeof document>"u"?"ar":(document.documentElement.lang||"ar").toLowerCase().startsWith("en")?"en":"ar"}class L{extras=c(be,{optional:!0});map=new Map;constructor(){for(const e of this.extras??[])this.register(e)}register(e){this.map.set(e.type,e.component)}get(e){return this.map.get(e)??null}hasCustom(e){return this.map.has(e)}isBuiltIn(e){return rt.has(e)}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:L,deps:[],target:l.ɵɵFactoryTarget.Injectable});static ɵprov=l.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:L,providedIn:"root"})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:L,decorators:[{type:Y,args:[{providedIn:"root"}]}],ctorParameters:()=>[]});const rt=new Set(["text","email","textarea","select","multiselect","checkbox","radio","phone","file","multifile","multifilestaged","date","chips","toggle","hidden","number","otp","password"]),Ae={resolveLabel(i,e){return e==="en"?i.en:i.ar}},Ee={getToken:()=>x(null)},Me={lookup:()=>x([])},lt=new Set(["__proto__","constructor","prototype"]);function F(i,e){const t=i.trim();if(!t)throw new Error("DGA dynamic form: empty HTTP URL");if(t.startsWith("/")&&!t.startsWith("//"))return t.replace(/\/$/,"")||"/";let a;try{a=new URL(t)}catch{throw new Error(`DGA dynamic form: invalid HTTP URL "${i}"`)}if(a.protocol!=="http:"&&a.protocol!=="https:")throw new Error(`DGA dynamic form: blocked URL protocol "${a.protocol}"`);if(e?.length&&!e.some(o=>{try{return new URL(o).origin===a.origin}catch{return o===a.origin}}))throw new Error(`DGA dynamic form: origin not allowlisted "${a.origin}"`);return a.toString().replace(/\/$/,"")}function Pe(i=c(S),e={}){return{lookup(t){try{const n=`${F(t.domain,e.allowedOrigins)}/lookup/${encodeURIComponent(t.name)}`,o={};if(t.parentId!=null&&t.parentId!==""&&(o.parentId=String(t.parentId)),t.search){const r=t.searchParamName||"q";o[r]=t.search}return i.get(n,{params:o}).pipe(Ue(r=>Array.isArray(r)?r.map(st):[]))}catch(a){return ge(()=>a)}}}}function st(i){const e=i??{},t=e.DisplayName??e.UserName??e.Email,a=e.value??e.id??e.Id??e.Code??t??"",n=String(e.labelEn??e.titleEn??e.en??e.TitleEn??e.Name??e.name??t??a),o=String(e.labelAr??e.titleAr??e.ar??e.TitleAr??e.NameAr??e.nameAr??n),r={value:a,label:{en:n,ar:o}};for(const[s,d]of Object.entries(e))lt.has(s)||s==="value"||s==="label"||s==="id"||s==="Id"||s==="labelEn"||s==="titleEn"||s==="en"||s==="TitleEn"||s==="labelAr"||s==="titleAr"||s==="ar"||s==="TitleAr"||(r[s]=d);return e.disabled!=null&&(r.disabled=!!e.disabled),e.requiresTextInput!=null&&(r.requiresTextInput=!!e.requiresTextInput),e.description&&typeof e.description=="object"&&(r.description=e.description),r}function Ve(i=c(S),e={}){return{submit(t){try{const a=F(t.endpoint,e.allowedOrigins),n=new He(t.headers??{}),o=t.method.toUpperCase();return o==="GET"?i.get(a,{headers:n}):o==="DELETE"?i.delete(a,{headers:n,body:t.body}):o==="PUT"?i.put(a,t.body,{headers:n}):o==="PATCH"?i.patch(a,t.body,{headers:n}):i.post(a,t.body,{headers:n})}catch(a){return ge(()=>a)}}}}function Re(i=c(je)){return{show(e){i.show({title:e.title,message:e.message,variant:e.variant??"info"})}}}function dt(i={}){const e=i.httpLookup??!1,t=i.httpSubmit??!0,a=i.dgaToast??!0,n=i.requireAllowedOrigins??!1,o={allowedOrigins:i.allowedOrigins};if(n&&(e||t)&&!i.allowedOrigins?.length)throw new Error("DGA dynamic form: provideDgaDynamicForm({ allowedOrigins: [...] }) is required when requireAllowedOrigins is true. ");return ze()&&(e||t)&&!i.allowedOrigins?.length&&console.warn("[dga-dynamic-form] HTTP adapters enabled without allowedOrigins. Absolute schema URLs can reach any https host — set allowedOrigins (and requireAllowedOrigins: true) for CMS schemas."),Be([{provide:$,useValue:o},{provide:U,useValue:Ae},{provide:_,useValue:Ee},{provide:z,useFactory:()=>e?Pe(c(S),o):Me},{provide:E,useFactory:()=>t?Ve(c(S),o):{submit:()=>x(null)}},{provide:B,useFactory:()=>a?Re():{show:()=>{}}}])}function ut(i,e,t=""){return i?e==="en"?i.en:i.ar:t}class v{accept=u("",...ngDevMode?[{debugName:"accept"}]:[]);maxFileSize=u(0,...ngDevMode?[{debugName:"maxFileSize"}]:[]);maxFiles=u(0,...ngDevMode?[{debugName:"maxFiles"}]:[]);uploadEndpoint=u("",...ngDevMode?[{debugName:"uploadEndpoint"}]:[]);deleteEndpoint=u("",...ngDevMode?[{debugName:"deleteEndpoint"}]:[]);deleteMethod=u("DELETE",...ngDevMode?[{debugName:"deleteMethod"}]:[]);hint=u("Drag files here or browse",...ngDevMode?[{debugName:"hint"}]:[]);browseLabel=u("Choose files",...ngDevMode?[{debugName:"browseLabel"}]:[]);removeLabel=u("Remove",...ngDevMode?[{debugName:"removeLabel"}]:[]);downloadLabel=u("Download",...ngDevMode?[{debugName:"downloadLabel"}]:[]);uploadingLabel=u("Uploading…",...ngDevMode?[{debugName:"uploadingLabel"}]:[]);errorLabel=u("Upload failed",...ngDevMode?[{debugName:"errorLabel"}]:[]);disabledInput=u(!1,{...ngDevMode?{debugName:"disabledInput"}:{},alias:"disabled",transform:J});cvaDisabled=m(!1,...ngDevMode?[{debugName:"cvaDisabled"}]:[]);disabled=p(()=>this.disabledInput()||this.cvaDisabled(),...ngDevMode?[{debugName:"disabled"}]:[]);fileDownload=g();http=c(S);httpSecurity=c($,{optional:!0});files=m([],...ngDevMode?[{debugName:"files"}]:[]);dragging=m(!1,...ngDevMode?[{debugName:"dragging"}]:[]);onChange=()=>{};onTouched=()=>{};atLimit=p(()=>{const e=this.maxFiles();return e>0&&this.files().length>=e},...ngDevMode?[{debugName:"atLimit"}]:[]);onDragOver(e){e.preventDefault(),!this.disabled()&&!this.atLimit()&&this.dragging.set(!0)}onDrop(e){if(e.preventDefault(),this.dragging.set(!1),this.disabled()||this.atLimit())return;const t=e.dataTransfer?.files;t?.length&&this.addFiles(Array.from(t))}onPick(e){const t=e.target;t.files?.length&&this.addFiles(Array.from(t.files)),t.value=""}remove(e){if(e.status==="uploading")return;const t=this.isExisting(e),a=this.deleteEndpoint();if(!t&&a&&e.fileId){const n=a.includes(":id")?a.replace(":id",encodeURIComponent(e.fileId)):a.includes(":fileId")?a.replace(":fileId",encodeURIComponent(e.fileId)):a;try{const o=F(n,this.httpSecurity?.allowedOrigins);this.deleteMethod()==="POST"?this.http.post(o,{FileName:e.fileName}).subscribe({complete:()=>this.commit(this.files().filter(r=>r!==e))}):this.http.delete(o).subscribe({complete:()=>this.commit(this.files().filter(r=>r!==e))});return}catch{}}this.commit(this.files().filter(n=>n!==e))}onDownload(e){this.isExisting(e)&&this.fileDownload.emit({Id:Number(e.fileId),FileName:e.fileName,ContentType:""})}isExisting(e){return e.listKey?.startsWith("existing-")??!1}addFiles(e){const t=this.maxFileSize(),a=this.maxFiles();let n=a>0?a-this.files().length:e.length;if(!(n<=0))for(const o of e.slice(0,n))t>0&&o.size>t||this.uploadOne(o)}uploadOne(e){const t=`new-${e.name}-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,a={fileId:"",fileName:e.name,sizeBytes:e.size,status:"uploading",listKey:t},n=[...this.files(),a];this.files.set(n),this.onChange(n);const o=this.uploadEndpoint();if(!o){a.status="success",a.fileId=t,this.commit([...this.files()]);return}try{const r=F(o,this.httpSecurity?.allowedOrigins),s=new FormData;s.append("file",e),this.http.post(r,s).subscribe({next:d=>{a.status="success",a.fileId=d.fileId??t,d.fileName&&(a.fileName=d.fileName),this.commit([...this.files()])},error:d=>{a.status="error",a.errorMessage=d?.message??this.errorLabel(),this.commit([...this.files()])}})}catch(r){a.status="error",a.errorMessage=r?.message??this.errorLabel(),this.commit([...this.files()])}}commit(e){this.files.set(e),this.onChange(e),this.onTouched()}writeValue(e){const a=(Array.isArray(e)?e:[]).map(n=>{if("Id"in n&&"FileName"in n){const o=n;return{fileId:String(o.Id),fileName:o.FileName,status:"success",listKey:`existing-${o.Id}`}}return n});this.files.set(a)}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){this.cvaDisabled.set(e)}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:v,deps:[],target:l.ɵɵFactoryTarget.Component});static ɵcmp=l.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"22.0.7",type:v,isStandalone:!0,selector:"dga-dynamic-form-staged-upload",inputs:{accept:{classPropertyName:"accept",publicName:"accept",isSignal:!0,isRequired:!1,transformFunction:null},maxFileSize:{classPropertyName:"maxFileSize",publicName:"maxFileSize",isSignal:!0,isRequired:!1,transformFunction:null},maxFiles:{classPropertyName:"maxFiles",publicName:"maxFiles",isSignal:!0,isRequired:!1,transformFunction:null},uploadEndpoint:{classPropertyName:"uploadEndpoint",publicName:"uploadEndpoint",isSignal:!0,isRequired:!1,transformFunction:null},deleteEndpoint:{classPropertyName:"deleteEndpoint",publicName:"deleteEndpoint",isSignal:!0,isRequired:!1,transformFunction:null},deleteMethod:{classPropertyName:"deleteMethod",publicName:"deleteMethod",isSignal:!0,isRequired:!1,transformFunction:null},hint:{classPropertyName:"hint",publicName:"hint",isSignal:!0,isRequired:!1,transformFunction:null},browseLabel:{classPropertyName:"browseLabel",publicName:"browseLabel",isSignal:!0,isRequired:!1,transformFunction:null},removeLabel:{classPropertyName:"removeLabel",publicName:"removeLabel",isSignal:!0,isRequired:!1,transformFunction:null},downloadLabel:{classPropertyName:"downloadLabel",publicName:"downloadLabel",isSignal:!0,isRequired:!1,transformFunction:null},uploadingLabel:{classPropertyName:"uploadingLabel",publicName:"uploadingLabel",isSignal:!0,isRequired:!1,transformFunction:null},errorLabel:{classPropertyName:"errorLabel",publicName:"errorLabel",isSignal:!0,isRequired:!1,transformFunction:null},disabledInput:{classPropertyName:"disabledInput",publicName:"disabled",isSignal:!0,isRequired:!1,transformFunction:null}},outputs:{fileDownload:"fileDownload"},host:{classAttribute:"block w-full"},providers:[ae(v)],ngImport:l,template:`
1
+ import*as l from"@angular/core";import{InjectionToken as x,inject as p,Injectable as X,isDevMode as je,makeEnvironmentProviders as We,input as c,booleanAttribute as Z,signal as f,computed as u,output as h,ChangeDetectionStrategy as R,Component as V,DestroyRef as ee,effect as N,untracked as q}from"@angular/core";import{NgComponentOutlet as te,NgTemplateOutlet as ae}from"@angular/common";import*as b from"@angular/forms";import{Validators as v,ReactiveFormsModule as O,FormsModule as ie,FormBuilder as Ke}from"@angular/forms";import{switchMap as ne,catchError as He,map as Ye,startWith as Je}from"rxjs/operators";import{takeUntilDestroyed as A}from"@angular/core/rxjs-interop";import{DgaToastService as Qe,DgaButton as E,DgaIcon as C,dgaProvideValueAccessor as oe,DgaField as re,DgaInput as le,DgaSelect as se,DgaCheckbox as de,DgaRadio as ce,DgaPhoneInput as ue,DgaChipInput as pe,DgaSwitch as fe,DgaUpload as me,DgaNumberInput as ge,DgaOtp as he,DgaAlert as be,DgaBadge as ve,DgaCard as ye,DgaProgress as xe,DgaStepper as De}from"@a.nemreen/dga-ui";import{of as D,throwError as we,startWith as Xe,catchError as Ze}from"rxjs";import{HttpClient as L,HttpHeaders as et}from"@angular/common/http";import{DgaDatePicker as Se}from"@a.nemreen/dga-ui/date-picker";const B=new x("DGA_LOOKUP_ADAPTER"),M=new x("DGA_SUBMIT_ADAPTER"),$=new x("DGA_FORM_TOAST_ADAPTER"),_=new x("DGA_CAPTCHA_ADAPTER"),U=new x("DGA_HTTP_SECURITY"),G=new x("DGA_FORM_I18N_ADAPTER"),Ce=new x("DGA_DYNAMIC_FORM_FIELD_REGISTRY"),Le=[["StartDate","EndDate"],["UseCaseStartDate","UseCaseEndDate"],["startDate","endDate"]];function Ie(i){return e=>e.value&&i.value!==e.value?{passwordMismatch:!0}:null}function tt(i){return e=>{if(!e.value||!i.value)return null;const t=new Date(e.value),a=new Date(i.value),n=new Date(a);return n.setDate(n.getDate()+1),t>=n?null:{endDateAfterStartDate:!0}}}function at(i){const e=(i??"").trim();if(e)return e.startsWith("+")?e:`+${e}`}function ke(i){const e=[...new Set((i??[]).map(t=>at(t?.code)).filter(t=>!!t&&t!=="+966"))].sort((t,a)=>a.length-t.length);return t=>{const a=(t.value??"").toString().trim();if(!a||!e.length)return j(t);const n=e.find(r=>a.startsWith(r));if(!n)return j(t);const o=a.slice(n.length).replace(/\s+/g,"");return o?/^\d+$/.test(o)?null:{invalidFormat:!0}:null}}function j(i){const e=(i.value??"").toString().trim();if(!e)return null;let t=e;return e.startsWith("+966")?t=e.slice(4):e.startsWith("966")&&(t=e.slice(3)),/^\d+$/.test(t)?t.startsWith("0")?t.length===10?null:{invalidLength:!0}:t.startsWith("5")?t.length===9?null:{invalidLength:!0}:{invalidPrefix:!0}:{invalidFormat:!0}}function it(i){return e=>{if(!e.value)return null;const t=new Date(e.value),a=new Date(i);return t>=a?null:{minDate:!0}}}function nt(i){return e=>{if(!e.value)return null;const t=new Date(e.value),a=new Date(i);return t<=a?null:{maxDate:!0}}}function ot(i){const e=i.value;return Array.isArray(e)&&e.some(a=>a&&typeof a=="object"&&a.status==="uploading")?{uploadInProgress:!0}:null}function Te(i,e){const t=i.get("password"),a=i.get("confirmPassword");t&&a&&(a.addValidators(Ie(t)),a.updateValueAndValidity({emitEvent:!1}));for(const[n,o]of Le){const r=i.get(n),s=i.get(o);if(!r||!s)continue;s.addValidators(tt(r));const d=e.find(S=>S.name===o),g=S=>{if(d?.validation){if(S){const Ge=new Date(S),z=new Date(Ge);z.setDate(z.getDate()+1),d.validation.minDate=z.toISOString().split("T")[0]}else delete d.validation.minDate;s.updateValueAndValidity({emitEvent:!1})}};g(r.value),r.valueChanges.subscribe(S=>g(S))}}const Fe=["checkbox","multiselect","chips","file","multifile","multifilestaged"],rt=["toggle"];function Ne(i){return i.value!==void 0?i.value:Fe.includes(i.type)?[]:rt.includes(i.type)?!1:""}function W(i,e){const t=[],a=i.validation;(e||i.required||a?.required)&&(Fe.includes(i.type)?t.push(lt):i.type==="toggle"?t.push(v.requiredTrue):t.push(v.required)),(i.type==="email"||a?.pattern==="email")&&t.push(v.email);const n=a?.minLength;n!=null&&t.push(v.minLength(n));const o=a?.maxLength??i.maxLength;if(o!=null&&t.push(v.maxLength(o)),a?.pattern&&a.pattern!=="email"){const r=a.pattern;if(typeof r=="string"&&r.length>0&&r.length<=200)try{t.push(v.pattern(r))}catch{}}return a?.min!=null&&t.push(v.min(a.min)),a?.max!=null&&t.push(v.max(a.max)),i.maxChips!=null&&i.type==="chips"&&t.push(st(i.maxChips)),i.type==="phone"&&t.push(ke(i.countries)),i.type==="multifilestaged"&&t.push(ot),a?.minDate&&t.push(it(a.minDate)),a?.maxDate&&t.push(nt(a.maxDate)),t}function lt(i){const e=i.value;return Array.isArray(e)&&e.length>0?null:{required:!0}}function st(i){return e=>{const t=e.value;return Array.isArray(t)&&t.length>i?{maxChips:{max:i,actual:t.length}}:null}}function Oe(i,e,t=!0){const a=!!e.disabled;return i.control({value:Ne(e),disabled:a},{validators:W(e,t&&!e.requiredWhen)})}function Ae(i,e){const t={};for(const a of e){if(a.hidden&&a.type==="hidden"){t[a.name]=i.control(a.value??"");continue}t[a.name]=Oe(i,a)}return i.group(t)}function Ee(i,e){return e?.length?e.flatMap(t=>t.fields):i}function K(i,e,t){i.setValidators(W(e,t)),i.updateValueAndValidity({emitEvent:!1})}function H(i,e){return Array.isArray(i)?i.some(t=>t==e):i==e}function dt(i){return i.label?.en??""}function ct(i){return i.label?.ar??""}function P(i,e,t,a){return Pe(a?.(i)??{name:i,type:"select",label:{en:"",ar:""}},e,t?.(i))}function ut(i,e,t,a,n){if(!i.showOnTitleEn&&!i.showOnTitleAr||t==null||t==="")return!1;const o=P(e,t,a,n);if(!o)return!1;const r=i.showOnTitleEn?dt(o)===i.showOnTitleEn:!0,s=i.showOnTitleAr?ct(o)===i.showOnTitleAr:!0;return r&&s}function Me(i,e,t,a){if(!i.extraField)return!0;const n=i.extraFieldTrigger||i.dependsOn;if(!n)return!1;const o=e[n];return P(n,o,t,a)?.requiresTextInput===!0}function Pe(i,e,t){return i?[...t??[],...i.options??[],...i.groupedOptions?.flatMap(n=>n.options)??[]].find(n=>n.value==e):void 0}function Y(i,e,t,a){if(i.hidden&&i.type!=="hidden"||i.type==="hidden")return!1;if(!i.dependsOn&&!i.extraField)return!0;const n=i.dependsOn??i.extraFieldTrigger;if(!n)return Me(i,e,t,a);const o=e[n];if(i.showOnValues?.length&&!i.showOnValues.some(r=>H(o,r))||(i.showOnTitleEn||i.showOnTitleAr)&&!ut(i,n,o,t,a))return!1;if(i.showWhenOptionProperty){const r=P(n,o,t,a),s=i.showWhenOptionProperty.property;if(!r||r[s]!==i.showWhenOptionProperty.value)return!1}return!(i.extraField&&!Me(i,e,t,a)||i.dependsOn&&!i.showOnValues?.length&&!i.showWhenOptionProperty&&!i.showOnTitleEn&&!i.showOnTitleAr&&!i.extraField&&(o==null||o===""||Array.isArray(o)&&o.length===0))}function J(i,e,t,a){const n=i.requiredWhen;if(!n)return!!i.required;const o=e[n.field];if(n.optionProperty!=null){const r=P(n.field,o,t,a);return!!r&&r[n.optionProperty]===n.optionPropertyValue}return n.values?.length?n.values.some(r=>H(o,r)):n.value!==void 0?H(o,n.value):o!=null&&o!==""}const pt=new Set(["otp","file"]),ze=/password|passwd|secret|token|otp|national.?id|ssn|pin$/i;class I{submitAdapter=p(M,{optional:!0});captcha=p(_,{optional:!0});buildPayload(e,t,a){const n=new Set(a.filter(r=>r.excludeFromPayload).map(r=>r.name));let o={};for(const[r,s]of Object.entries(t)){if(n.has(r))continue;const d=e.fieldMapping?.[r]??r;o[d]=s}return e.payloadTransformer?e.payloadTransformer(o):o}hasFileValues(e){return Object.values(e).some(t=>t instanceof File||Array.isArray(t)&&t.some(a=>a instanceof File||a?.file instanceof File))}toFormData(e){const t=new FormData;for(const[a,n]of Object.entries(e))if(n!=null)if(n instanceof File)t.append(a,n);else if(Array.isArray(n))for(const o of n)o instanceof File?t.append(a,o):o?.file instanceof File?t.append(a,o.file,o.name):t.append(a,typeof o=="string"?o:JSON.stringify(o));else typeof n=="object"?t.append(a,JSON.stringify(n)):t.append(a,String(n));return t}submitForm(e,t,a){if(!e.endpoint||!this.submitAdapter)return D(t);const n=this.buildPayload(e,t,a),o=n instanceof FormData?n:this.hasFileValues(n)?this.toFormData(n):n,r={};return e.idempotencyKey&&(r["Idempotency-Key"]=e.idempotencyKey),(this.captcha?.getToken("submit")??D(null)).pipe(ne(d=>(d&&(r["X-Recaptcha-Token"]=d),D({endpoint:e.endpoint,method:e.method??"POST",body:o,headers:r})))).pipe(ne(d=>this.submitAdapter.submit(d)),He(d=>{throw d}))}saveDraft(e,t,a=[]){if(typeof localStorage>"u")return;const n=new Map(a.map(r=>[r.name,r])),o={};for(const[r,s]of Object.entries(t)){const d=n.get(r);d&&this.isDraftBlocked(d)||!d&&ze.test(r)||s instanceof File||(o[r]=s)}localStorage.setItem(this.storageKey(e),JSON.stringify(o))}loadDraft(e){if(typeof localStorage>"u")return null;const t=localStorage.getItem(this.storageKey(e));if(!t)return null;try{return JSON.parse(t)}catch{return null}}clearDraft(e){typeof localStorage>"u"||localStorage.removeItem(this.storageKey(e))}isDraftBlocked(e){return e.excludeFromDraft||e.sensitive||e.excludeFromPayload||pt.has(e.type)?!0:ze.test(e.name)}storageKey(e){return`dga-dynamic-form:${e}`}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:I,deps:[],target:l.ɵɵFactoryTarget.Injectable});static ɵprov=l.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:I,providedIn:"root"})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:I,decorators:[{type:X,args:[{providedIn:"root"}]}]});function m(i,e,t=""){if(!i)return t;const a=i[e];return a||(i.en?i.en:i.ar?i.ar:Object.values(i).find(o=>!!o)||t)}function Re(){return typeof document>"u"?"ar":(document.documentElement.lang||"ar").toLowerCase().split("-")[0]||"ar"}class k{extras=p(Ce,{optional:!0});map=new Map;constructor(){for(const e of this.extras??[])this.register(e)}register(e){this.map.set(e.type,e.component)}get(e){return this.map.get(e)??null}hasCustom(e){return this.map.has(e)}isBuiltIn(e){return ft.has(e)}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:k,deps:[],target:l.ɵɵFactoryTarget.Injectable});static ɵprov=l.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:k,providedIn:"root"})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:k,decorators:[{type:X,args:[{providedIn:"root"}]}],ctorParameters:()=>[]});const ft=new Set(["text","email","textarea","select","multiselect","checkbox","radio","phone","file","multifile","multifilestaged","date","chips","toggle","hidden","number","otp","password"]),Ve={resolveLabel(i,e){return m(i,e)}},qe={getToken:()=>D(null)},Be={lookup:()=>D([])},mt=new Set(["__proto__","constructor","prototype"]);function T(i,e){const t=i.trim();if(!t)throw new Error("DGA dynamic form: empty HTTP URL");if(t.startsWith("/")&&!t.startsWith("//"))return t.replace(/\/$/,"")||"/";let a;try{a=new URL(t)}catch{throw new Error(`DGA dynamic form: invalid HTTP URL "${i}"`)}if(a.protocol!=="http:"&&a.protocol!=="https:")throw new Error(`DGA dynamic form: blocked URL protocol "${a.protocol}"`);if(e?.length&&!e.some(o=>{try{return new URL(o).origin===a.origin}catch{return o===a.origin}}))throw new Error(`DGA dynamic form: origin not allowlisted "${a.origin}"`);return a.toString().replace(/\/$/,"")}function $e(i=p(L),e={}){return{lookup(t){try{const n=`${T(t.domain,e.allowedOrigins)}/lookup/${encodeURIComponent(t.name)}`,o={};if(t.parentId!=null&&t.parentId!==""&&(o.parentId=String(t.parentId)),t.search){const r=t.searchParamName||"q";o[r]=t.search}return i.get(n,{params:o}).pipe(Ye(r=>Array.isArray(r)?r.map(gt):[]))}catch(a){return we(()=>a)}}}}function gt(i){const e=i??{},t=e.DisplayName??e.UserName??e.Email,a=e.value??e.id??e.Id??e.Code??t??"",n=String(e.labelEn??e.titleEn??e.en??e.TitleEn??e.Name??e.name??t??a),o=String(e.labelAr??e.titleAr??e.ar??e.TitleAr??e.NameAr??e.nameAr??n),r={value:a,label:{en:n,ar:o}};for(const[s,d]of Object.entries(e))mt.has(s)||s==="value"||s==="label"||s==="id"||s==="Id"||s==="labelEn"||s==="titleEn"||s==="en"||s==="TitleEn"||s==="labelAr"||s==="titleAr"||s==="ar"||s==="TitleAr"||(r[s]=d);return e.disabled!=null&&(r.disabled=!!e.disabled),e.requiresTextInput!=null&&(r.requiresTextInput=!!e.requiresTextInput),e.description&&typeof e.description=="object"&&(r.description=e.description),r}function _e(i=p(L),e={}){return{submit(t){try{const a=T(t.endpoint,e.allowedOrigins),n=new et(t.headers??{}),o=t.method.toUpperCase();return o==="GET"?i.get(a,{headers:n}):o==="DELETE"?i.delete(a,{headers:n,body:t.body}):o==="PUT"?i.put(a,t.body,{headers:n}):o==="PATCH"?i.patch(a,t.body,{headers:n}):i.post(a,t.body,{headers:n})}catch(a){return we(()=>a)}}}}function Ue(i=p(Qe)){return{show(e){i.show({title:e.title,message:e.message,variant:e.variant??"info"})}}}function ht(i={}){const e=i.httpLookup??!1,t=i.httpSubmit??!0,a=i.dgaToast??!0,n=i.requireAllowedOrigins??!1,o={allowedOrigins:i.allowedOrigins};if(n&&(e||t)&&!i.allowedOrigins?.length)throw new Error("DGA dynamic form: provideDgaDynamicForm({ allowedOrigins: [...] }) is required when requireAllowedOrigins is true. ");return je()&&(e||t)&&!i.allowedOrigins?.length&&console.warn("[dga-dynamic-form] HTTP adapters enabled without allowedOrigins. Absolute schema URLs can reach any https host — set allowedOrigins (and requireAllowedOrigins: true) for CMS schemas."),We([{provide:U,useValue:o},{provide:G,useValue:Ve},{provide:_,useValue:qe},{provide:B,useFactory:()=>e?$e(p(L),o):Be},{provide:M,useFactory:()=>t?_e(p(L),o):{submit:()=>D(null)}},{provide:$,useFactory:()=>a?Ue():{show:()=>{}}}])}function bt(i,e,t=""){return m(i,e,t)}class y{accept=c("",...ngDevMode?[{debugName:"accept"}]:[]);maxFileSize=c(0,...ngDevMode?[{debugName:"maxFileSize"}]:[]);maxFiles=c(0,...ngDevMode?[{debugName:"maxFiles"}]:[]);uploadEndpoint=c("",...ngDevMode?[{debugName:"uploadEndpoint"}]:[]);deleteEndpoint=c("",...ngDevMode?[{debugName:"deleteEndpoint"}]:[]);deleteMethod=c("DELETE",...ngDevMode?[{debugName:"deleteMethod"}]:[]);hint=c("Drag files here or browse",...ngDevMode?[{debugName:"hint"}]:[]);browseLabel=c("Choose files",...ngDevMode?[{debugName:"browseLabel"}]:[]);removeLabel=c("Remove",...ngDevMode?[{debugName:"removeLabel"}]:[]);downloadLabel=c("Download",...ngDevMode?[{debugName:"downloadLabel"}]:[]);uploadingLabel=c("Uploading…",...ngDevMode?[{debugName:"uploadingLabel"}]:[]);errorLabel=c("Upload failed",...ngDevMode?[{debugName:"errorLabel"}]:[]);disabledInput=c(!1,{...ngDevMode?{debugName:"disabledInput"}:{},alias:"disabled",transform:Z});cvaDisabled=f(!1,...ngDevMode?[{debugName:"cvaDisabled"}]:[]);disabled=u(()=>this.disabledInput()||this.cvaDisabled(),...ngDevMode?[{debugName:"disabled"}]:[]);fileDownload=h();http=p(L);httpSecurity=p(U,{optional:!0});files=f([],...ngDevMode?[{debugName:"files"}]:[]);dragging=f(!1,...ngDevMode?[{debugName:"dragging"}]:[]);onChange=()=>{};onTouched=()=>{};atLimit=u(()=>{const e=this.maxFiles();return e>0&&this.files().length>=e},...ngDevMode?[{debugName:"atLimit"}]:[]);onDragOver(e){e.preventDefault(),!this.disabled()&&!this.atLimit()&&this.dragging.set(!0)}onDrop(e){if(e.preventDefault(),this.dragging.set(!1),this.disabled()||this.atLimit())return;const t=e.dataTransfer?.files;t?.length&&this.addFiles(Array.from(t))}onPick(e){const t=e.target;t.files?.length&&this.addFiles(Array.from(t.files)),t.value=""}remove(e){if(e.status==="uploading")return;const t=this.isExisting(e),a=this.deleteEndpoint();if(!t&&a&&e.fileId){const n=a.includes(":id")?a.replace(":id",encodeURIComponent(e.fileId)):a.includes(":fileId")?a.replace(":fileId",encodeURIComponent(e.fileId)):a;try{const o=T(n,this.httpSecurity?.allowedOrigins);this.deleteMethod()==="POST"?this.http.post(o,{FileName:e.fileName}).subscribe({complete:()=>this.commit(this.files().filter(r=>r!==e))}):this.http.delete(o).subscribe({complete:()=>this.commit(this.files().filter(r=>r!==e))});return}catch{}}this.commit(this.files().filter(n=>n!==e))}onDownload(e){this.isExisting(e)&&this.fileDownload.emit({Id:Number(e.fileId),FileName:e.fileName,ContentType:""})}isExisting(e){return e.listKey?.startsWith("existing-")??!1}addFiles(e){const t=this.maxFileSize(),a=this.maxFiles();let n=a>0?a-this.files().length:e.length;if(!(n<=0))for(const o of e.slice(0,n))t>0&&o.size>t||this.uploadOne(o)}uploadOne(e){const t=`new-${e.name}-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,a={fileId:"",fileName:e.name,sizeBytes:e.size,status:"uploading",listKey:t},n=[...this.files(),a];this.files.set(n),this.onChange(n);const o=this.uploadEndpoint();if(!o){a.status="success",a.fileId=t,this.commit([...this.files()]);return}try{const r=T(o,this.httpSecurity?.allowedOrigins),s=new FormData;s.append("file",e),this.http.post(r,s).subscribe({next:d=>{a.status="success",a.fileId=d.fileId??t,d.fileName&&(a.fileName=d.fileName),this.commit([...this.files()])},error:d=>{a.status="error",a.errorMessage=d?.message??this.errorLabel(),this.commit([...this.files()])}})}catch(r){a.status="error",a.errorMessage=r?.message??this.errorLabel(),this.commit([...this.files()])}}commit(e){this.files.set(e),this.onChange(e),this.onTouched()}writeValue(e){const a=(Array.isArray(e)?e:[]).map(n=>{if("Id"in n&&"FileName"in n){const o=n;return{fileId:String(o.Id),fileName:o.FileName,status:"success",listKey:`existing-${o.Id}`}}return n});this.files.set(a)}registerOnChange(e){this.onChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){this.cvaDisabled.set(e)}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:y,deps:[],target:l.ɵɵFactoryTarget.Component});static ɵcmp=l.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"22.1.5",type:y,isStandalone:!0,selector:"dga-dynamic-form-staged-upload",inputs:{accept:{classPropertyName:"accept",publicName:"accept",isSignal:!0,isRequired:!1,transformFunction:null},maxFileSize:{classPropertyName:"maxFileSize",publicName:"maxFileSize",isSignal:!0,isRequired:!1,transformFunction:null},maxFiles:{classPropertyName:"maxFiles",publicName:"maxFiles",isSignal:!0,isRequired:!1,transformFunction:null},uploadEndpoint:{classPropertyName:"uploadEndpoint",publicName:"uploadEndpoint",isSignal:!0,isRequired:!1,transformFunction:null},deleteEndpoint:{classPropertyName:"deleteEndpoint",publicName:"deleteEndpoint",isSignal:!0,isRequired:!1,transformFunction:null},deleteMethod:{classPropertyName:"deleteMethod",publicName:"deleteMethod",isSignal:!0,isRequired:!1,transformFunction:null},hint:{classPropertyName:"hint",publicName:"hint",isSignal:!0,isRequired:!1,transformFunction:null},browseLabel:{classPropertyName:"browseLabel",publicName:"browseLabel",isSignal:!0,isRequired:!1,transformFunction:null},removeLabel:{classPropertyName:"removeLabel",publicName:"removeLabel",isSignal:!0,isRequired:!1,transformFunction:null},downloadLabel:{classPropertyName:"downloadLabel",publicName:"downloadLabel",isSignal:!0,isRequired:!1,transformFunction:null},uploadingLabel:{classPropertyName:"uploadingLabel",publicName:"uploadingLabel",isSignal:!0,isRequired:!1,transformFunction:null},errorLabel:{classPropertyName:"errorLabel",publicName:"errorLabel",isSignal:!0,isRequired:!1,transformFunction:null},disabledInput:{classPropertyName:"disabledInput",publicName:"disabled",isSignal:!0,isRequired:!1,transformFunction:null}},outputs:{fileDownload:"fileDownload"},host:{classAttribute:"block w-full"},providers:[oe(y)],ngImport:l,template:`
2
2
  <div class="flex flex-col gap-md">
3
3
  <div
4
4
  class="flex flex-col items-center justify-center gap-md rounded-lg border border-dashed border-border
@@ -76,7 +76,7 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
76
76
  </ul>
77
77
  }
78
78
  </div>
79
- `,isInline:!0,dependencies:[{kind:"directive",type:A,selector:"button[dgaButton], a[dgaButton]",inputs:["variant","size","iconOnly","cooldownMs","loading","dgaButtonClass"]},{kind:"component",type:te,selector:"dga-icon",inputs:["name","glyph","size","tone","label"]}],changeDetection:l.ChangeDetectionStrategy.OnPush})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:v,decorators:[{type:R,args:[{selector:"dga-dynamic-form-staged-upload",standalone:!0,imports:[A,te],template:`
79
+ `,isInline:!0,dependencies:[{kind:"directive",type:E,selector:"button[dgaButton], a[dgaButton]",inputs:["variant","size","iconOnly","cooldownMs","loading","dgaButtonClass"]},{kind:"component",type:C,selector:"dga-icon",inputs:["name","glyph","size","tone","label"]}],changeDetection:l.ChangeDetectionStrategy.OnPush})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:y,decorators:[{type:V,args:[{selector:"dga-dynamic-form-staged-upload",standalone:!0,imports:[E,C],template:`
80
80
  <div class="flex flex-col gap-md">
81
81
  <div
82
82
  class="flex flex-col items-center justify-center gap-md rounded-lg border border-dashed border-border
@@ -154,7 +154,7 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
154
154
  </ul>
155
155
  }
156
156
  </div>
157
- `,changeDetection:V.OnPush,host:{class:"block w-full"},providers:[ae(v)]}]}],propDecorators:{accept:[{type:l.Input,args:[{isSignal:!0,alias:"accept",required:!1}]}],maxFileSize:[{type:l.Input,args:[{isSignal:!0,alias:"maxFileSize",required:!1}]}],maxFiles:[{type:l.Input,args:[{isSignal:!0,alias:"maxFiles",required:!1}]}],uploadEndpoint:[{type:l.Input,args:[{isSignal:!0,alias:"uploadEndpoint",required:!1}]}],deleteEndpoint:[{type:l.Input,args:[{isSignal:!0,alias:"deleteEndpoint",required:!1}]}],deleteMethod:[{type:l.Input,args:[{isSignal:!0,alias:"deleteMethod",required:!1}]}],hint:[{type:l.Input,args:[{isSignal:!0,alias:"hint",required:!1}]}],browseLabel:[{type:l.Input,args:[{isSignal:!0,alias:"browseLabel",required:!1}]}],removeLabel:[{type:l.Input,args:[{isSignal:!0,alias:"removeLabel",required:!1}]}],downloadLabel:[{type:l.Input,args:[{isSignal:!0,alias:"downloadLabel",required:!1}]}],uploadingLabel:[{type:l.Input,args:[{isSignal:!0,alias:"uploadingLabel",required:!1}]}],errorLabel:[{type:l.Input,args:[{isSignal:!0,alias:"errorLabel",required:!1}]}],disabledInput:[{type:l.Input,args:[{isSignal:!0,alias:"disabled",required:!1}]}],fileDownload:[{type:l.Output,args:["fileDownload"]}]}});class D{field=u.required(...ngDevMode?[{debugName:"field"}]:[]);form=u.required(...ngDevMode?[{debugName:"form"}]:[]);locale=u("ar",...ngDevMode?[{debugName:"locale"}]:[]);required=u(!1,{...ngDevMode?{debugName:"required"}:{},transform:J});error=u("",...ngDevMode?[{debugName:"error"}]:[]);runtimeOptions=u([],...ngDevMode?[{debugName:"runtimeOptions"}]:[]);consentBaseUrl=u(...ngDevMode?[void 0,{debugName:"consentBaseUrl"}]:[]);optionsLoaded=g();fileDownload=g();lookup=c(z,{optional:!0});http=c(S,{optional:!0});destroyRef=c(Q);loadedOptions=m([],...ngDevMode?[{debugName:"loadedOptions"}]:[]);controlId=`dga-df-${Math.random().toString(36).slice(2,9)}`;get control(){return this.form().controls[this.field().name]}labelText=p(()=>I(this.field().label,this.locale()),...ngDevMode?[{debugName:"labelText"}]:[]);hintText=p(()=>I(this.field().hint,this.locale()),...ngDevMode?[{debugName:"hintText"}]:[]);placeholderText=p(()=>I(this.field().placeholder,this.locale()),...ngDevMode?[{debugName:"placeholderText"}]:[]);resolvedOptions=p(()=>{const e=this.runtimeOptions();if(e.length)return e;const t=this.loadedOptions();return t.length?t:this.field().options??[]},...ngDevMode?[{debugName:"resolvedOptions"}]:[]);selectOptions=p(()=>this.resolvedOptions().map(e=>({value:String(e.value),label:this.optionLabel(e),disabled:!!e.disabled})),...ngDevMode?[{debugName:"selectOptions"}]:[]);ngOnInit(){const e=this.field();if(this.mapExistingFileValue(),e.consentServiceName&&this.consentBaseUrl()&&this.http&&this.fetchConsent(e),e.lookupDomain&&e.lookupName&&this.lookup){if(e.dependsOn&&e.useParentId!==!1){const t=this.form().get(e.dependsOn);if(t){t.valueChanges.pipe(We(t.value),T(this.destroyRef)).subscribe(a=>this.fetchLookup(e,a));return}}e.searchLookup||this.fetchLookup(e)}}mapExistingFileValue(){const e=this.field();if(e.type!=="file"&&e.type!=="multifile")return;const t=this.control.value;if(!Array.isArray(t)||!t.length)return;const a=t[0];if(!a||!("Id"in a)||!("FileName"in a))return;const n=t.map(o=>({id:String(o.Id),name:o.FileName,size:0}));this.control.setValue(n,{emitEvent:!1})}fetchConsent(e){const a=`${this.consentBaseUrl().replace(/\/$/,"")}/consents/${encodeURIComponent(e.consentServiceName)}`;this.http.get(a).pipe(Ke(()=>x(null)),T(this.destroyRef)).subscribe(n=>{if(!n?.Content)return;const o={en:n.Content.En||n.Content.Ar||"",ar:n.Content.Ar||n.Content.En||""};if(e.type==="checkbox"){const r=e.options?.length?[...e.options]:[{value:!0,label:{en:"",ar:""}}];r[0]={...r[0],label:o},this.loadedOptions.set(r),this.optionsLoaded.emit({name:e.name,options:r})}})}fetchLookup(e,t,a){if(!this.lookup||!e.lookupDomain||!e.lookupName)return;const n=e.minSearchLength??(e.searchLookup?3:0);a!=null&&a.length<n||this.lookup.lookup({domain:e.lookupDomain,name:e.lookupName,parentId:e.useParentId!==!1&&t!=null&&t!==""?t:void 0,search:a||void 0,searchParamName:e.searchParamName}).pipe(T(this.destroyRef)).subscribe(o=>{const r=this.applyLookupFilter(o);this.loadedOptions.set(r),this.optionsLoaded.emit({name:e.name,options:r})})}optionLabel(e){return I(e.label,this.locale())}isPasswordField(){const e=this.field().name;return e==="password"||e==="confirmPassword"}isChecked(e){const t=this.control?.value;return Array.isArray(t)&&t.some(a=>a==e)}toggleCheckbox(e,t){const a=Array.isArray(this.control.value)?[...this.control.value]:[],n=t?a.some(o=>o==e)?a:[...a,e]:a.filter(o=>o!=e);this.control.setValue(n),this.control.markAsDirty()}onSelectSearchQuery(e){const t=this.field();t.searchLookup&&this.fetchLookup(t,this.form().get(t.dependsOn??"")?.value,e)}onStagedFileDownload(e){this.fileDownload.emit(e)}onRestrict(e){const t=this.field().inputRestriction;if(!t)return;const a=e.target;let n=a.value;t==="numbers"&&(n=n.replace(/\D+/g,"")),t==="english"&&(n=n.replace(/[^a-zA-Z0-9\s.,\-_/]/g,"")),t==="arabic"&&(n=n.replace(/[^\u0600-\u06FF0-9\s.,\-_/]/g,"")),n!==a.value&&(a.value=n,this.control.setValue(n))}applyLookupFilter(e){const t=this.field().lookupFilter;return t?e.filter(a=>a[t.property]===t.value):e}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:D,deps:[],target:l.ɵɵFactoryTarget.Component});static ɵcmp=l.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"22.0.7",type:D,isStandalone:!0,selector:"dga-dynamic-form-field",inputs:{field:{classPropertyName:"field",publicName:"field",isSignal:!0,isRequired:!0,transformFunction:null},form:{classPropertyName:"form",publicName:"form",isSignal:!0,isRequired:!0,transformFunction:null},locale:{classPropertyName:"locale",publicName:"locale",isSignal:!0,isRequired:!1,transformFunction:null},required:{classPropertyName:"required",publicName:"required",isSignal:!0,isRequired:!1,transformFunction:null},error:{classPropertyName:"error",publicName:"error",isSignal:!0,isRequired:!1,transformFunction:null},runtimeOptions:{classPropertyName:"runtimeOptions",publicName:"runtimeOptions",isSignal:!0,isRequired:!1,transformFunction:null},consentBaseUrl:{classPropertyName:"consentBaseUrl",publicName:"consentBaseUrl",isSignal:!0,isRequired:!1,transformFunction:null}},outputs:{optionsLoaded:"optionsLoaded",fileDownload:"fileDownload"},host:{properties:{"class.hidden":'field().type === "hidden"'},classAttribute:"block w-full"},ngImport:l,template:`
157
+ `,changeDetection:R.OnPush,host:{class:"block w-full"},providers:[oe(y)]}]}],propDecorators:{accept:[{type:l.Input,args:[{isSignal:!0,alias:"accept",required:!1}]}],maxFileSize:[{type:l.Input,args:[{isSignal:!0,alias:"maxFileSize",required:!1}]}],maxFiles:[{type:l.Input,args:[{isSignal:!0,alias:"maxFiles",required:!1}]}],uploadEndpoint:[{type:l.Input,args:[{isSignal:!0,alias:"uploadEndpoint",required:!1}]}],deleteEndpoint:[{type:l.Input,args:[{isSignal:!0,alias:"deleteEndpoint",required:!1}]}],deleteMethod:[{type:l.Input,args:[{isSignal:!0,alias:"deleteMethod",required:!1}]}],hint:[{type:l.Input,args:[{isSignal:!0,alias:"hint",required:!1}]}],browseLabel:[{type:l.Input,args:[{isSignal:!0,alias:"browseLabel",required:!1}]}],removeLabel:[{type:l.Input,args:[{isSignal:!0,alias:"removeLabel",required:!1}]}],downloadLabel:[{type:l.Input,args:[{isSignal:!0,alias:"downloadLabel",required:!1}]}],uploadingLabel:[{type:l.Input,args:[{isSignal:!0,alias:"uploadingLabel",required:!1}]}],errorLabel:[{type:l.Input,args:[{isSignal:!0,alias:"errorLabel",required:!1}]}],disabledInput:[{type:l.Input,args:[{isSignal:!0,alias:"disabled",required:!1}]}],fileDownload:[{type:l.Output,args:["fileDownload"]}]}});class w{field=c.required(...ngDevMode?[{debugName:"field"}]:[]);form=c.required(...ngDevMode?[{debugName:"form"}]:[]);locale=c("ar",...ngDevMode?[{debugName:"locale"}]:[]);required=c(!1,{...ngDevMode?{debugName:"required"}:{},transform:Z});error=c("",...ngDevMode?[{debugName:"error"}]:[]);runtimeOptions=c([],...ngDevMode?[{debugName:"runtimeOptions"}]:[]);consentBaseUrl=c(...ngDevMode?[void 0,{debugName:"consentBaseUrl"}]:[]);optionsLoaded=h();fileDownload=h();lookup=p(B,{optional:!0});http=p(L,{optional:!0});destroyRef=p(ee);loadedOptions=f([],...ngDevMode?[{debugName:"loadedOptions"}]:[]);controlId=`dga-df-${Math.random().toString(36).slice(2,9)}`;get control(){return this.form().controls[this.field().name]}labelText=u(()=>m(this.field().label,this.locale()),...ngDevMode?[{debugName:"labelText"}]:[]);hintText=u(()=>m(this.field().hint,this.locale()),...ngDevMode?[{debugName:"hintText"}]:[]);placeholderText=u(()=>m(this.field().placeholder,this.locale()),...ngDevMode?[{debugName:"placeholderText"}]:[]);phoneCountries=u(()=>(this.field().countries??[]).flatMap(e=>{const t=(e.code??"").trim();if(!t)return[];const a=t.startsWith("+")?t:`+${t}`;return[{code:a,label:(e.label??"").trim()||a,name:e.name}]}),...ngDevMode?[{debugName:"phoneCountries"}]:[]);resolvedOptions=u(()=>{const e=this.runtimeOptions();if(e.length)return e;const t=this.loadedOptions();if(t.length)return t;const a=this.field();return a.options?.length?a.options:a.groupedOptions?.flatMap(n=>n.options)??[]},...ngDevMode?[{debugName:"resolvedOptions"}]:[]);optionGroups=u(()=>{const e=this.runtimeOptions();if(e.length)return[{label:"",options:e}];const t=this.loadedOptions();if(t.length)return[{label:"",options:t}];const a=this.field();return a.groupedOptions?.length&&!a.options?.length?a.groupedOptions.map(n=>({label:m(n.groupLabel,this.locale()),options:n.options})):[{label:"",options:a.options??[]}]},...ngDevMode?[{debugName:"optionGroups"}]:[]);selectOptions=u(()=>this.resolvedOptions().map(e=>({value:String(e.value),label:this.optionLabel(e),disabled:!!e.disabled})),...ngDevMode?[{debugName:"selectOptions"}]:[]);ngOnInit(){const e=this.field();if(this.mapExistingFileValue(),e.consentServiceName&&this.consentBaseUrl()&&this.http&&this.fetchConsent(e),e.lookupDomain&&e.lookupName&&this.lookup){if(e.dependsOn&&e.useParentId!==!1){const t=this.form().get(e.dependsOn);if(t){t.valueChanges.pipe(Xe(t.value),A(this.destroyRef)).subscribe(a=>this.fetchLookup(e,a));return}}e.searchLookup||this.fetchLookup(e)}}mapExistingFileValue(){const e=this.field();if(e.type!=="file"&&e.type!=="multifile")return;const t=this.control.value;if(!Array.isArray(t)||!t.length)return;const a=t[0];if(!a||!("Id"in a)||!("FileName"in a))return;const n=t.map(o=>({id:String(o.Id),name:o.FileName,size:0}));this.control.setValue(n,{emitEvent:!1})}fetchConsent(e){const a=`${this.consentBaseUrl().replace(/\/$/,"")}/consents/${encodeURIComponent(e.consentServiceName)}`;this.http.get(a).pipe(Ze(()=>D(null)),A(this.destroyRef)).subscribe(n=>{if(!n?.Content)return;const o={en:n.Content.En||n.Content.Ar||"",ar:n.Content.Ar||n.Content.En||""};if(e.type==="checkbox"){const r=e.options?.length?[...e.options]:[{value:!0,label:{en:"",ar:""}}];r[0]={...r[0],label:o},this.loadedOptions.set(r),this.optionsLoaded.emit({name:e.name,options:r})}})}fetchLookup(e,t,a){if(!this.lookup||!e.lookupDomain||!e.lookupName)return;const n=e.minSearchLength??(e.searchLookup?3:0);a!=null&&a.length<n||this.lookup.lookup({domain:e.lookupDomain,name:e.lookupName,parentId:e.useParentId!==!1&&t!=null&&t!==""?t:void 0,search:a||void 0,searchParamName:e.searchParamName}).pipe(A(this.destroyRef)).subscribe(o=>{const r=this.applyLookupFilter(o);this.loadedOptions.set(r),this.optionsLoaded.emit({name:e.name,options:r})})}optionLabel(e){return m(e.label,this.locale())}optionDescription(e){return e.description?m(e.description,this.locale()):""}isCardsLayout(){return this.field().optionsLayout==="cards"}optionsContainerClass(){const e=this.field().optionsLayout;return e==="cards"?"grid grid-cols-1 gap-md sm:grid-cols-2":e==="grid"?"flex flex-wrap gap-lg":"flex flex-col gap-md"}choiceLabelClass(e){return this.isCardsLayout()?["flex min-h-7xl cursor-pointer items-start gap-lg rounded-lg border border-border bg-elevated p-xl text-start shadow-xs","transition-[border-color,box-shadow,background-color] hover:border-border-primary-light hover:shadow-sm","has-[:checked]:border-border-primary has-[:checked]:bg-primary-faint has-[:checked]:shadow-sm has-[:checked]:ring-2 has-[:checked]:ring-primary/20","has-[:focus-visible]:outline has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-focus",e.disabled||this.field().disabled?"pointer-events-none opacity-50":""].filter(Boolean).join(" "):"inline-flex max-w-full items-start gap-sm text-body-sm text-fg"}isPasswordField(){const e=this.field().name;return e==="password"||e==="confirmPassword"}isChecked(e){const t=this.control?.value;return Array.isArray(t)&&t.some(a=>a==e)}toggleCheckbox(e,t){const a=Array.isArray(this.control.value)?[...this.control.value]:[],n=t?a.some(o=>o==e)?a:[...a,e]:a.filter(o=>o!=e);this.control.setValue(n),this.control.markAsDirty()}onSelectSearchQuery(e){const t=this.field();t.searchLookup&&this.fetchLookup(t,this.form().get(t.dependsOn??"")?.value,e)}onStagedFileDownload(e){this.fileDownload.emit(e)}onRestrict(e){const t=this.field().inputRestriction;if(!t)return;const a=e.target;let n=a.value;t==="numbers"&&(n=n.replace(/\D+/g,"")),t==="english"&&(n=n.replace(/[^a-zA-Z0-9\s.,\-_/]/g,"")),t==="arabic"&&(n=n.replace(/[^\u0600-\u06FF0-9\s.,\-_/]/g,"")),n!==a.value&&(a.value=n,this.control.setValue(n))}applyLookupFilter(e){const t=this.field().lookupFilter;return t?e.filter(a=>a[t.property]===t.value):e}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:w,deps:[],target:l.ɵɵFactoryTarget.Component});static ɵcmp=l.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"22.1.5",type:w,isStandalone:!0,selector:"dga-dynamic-form-field",inputs:{field:{classPropertyName:"field",publicName:"field",isSignal:!0,isRequired:!0,transformFunction:null},form:{classPropertyName:"form",publicName:"form",isSignal:!0,isRequired:!0,transformFunction:null},locale:{classPropertyName:"locale",publicName:"locale",isSignal:!0,isRequired:!1,transformFunction:null},required:{classPropertyName:"required",publicName:"required",isSignal:!0,isRequired:!1,transformFunction:null},error:{classPropertyName:"error",publicName:"error",isSignal:!0,isRequired:!1,transformFunction:null},runtimeOptions:{classPropertyName:"runtimeOptions",publicName:"runtimeOptions",isSignal:!0,isRequired:!1,transformFunction:null},consentBaseUrl:{classPropertyName:"consentBaseUrl",publicName:"consentBaseUrl",isSignal:!0,isRequired:!1,transformFunction:null}},outputs:{optionsLoaded:"optionsLoaded",fileDownload:"fileDownload"},host:{properties:{"class.hidden":'field().type === "hidden"'},classAttribute:"block w-full"},ngImport:l,template:`
158
158
  @if (field().type !== 'hidden') {
159
159
  <dga-field
160
160
  [label]="labelText()"
@@ -207,6 +207,8 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
207
207
  [disabled]="!!field().disabled"
208
208
  [required]="required()"
209
209
  [controlId]="controlId"
210
+ [locale]="locale()"
211
+ [countries]="phoneCountries()"
210
212
  />
211
213
  }
212
214
  @case ('date') {
@@ -297,44 +299,77 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
297
299
  />
298
300
  }
299
301
  @case ('radio') {
300
- <div
301
- class="flex flex-wrap gap-lg"
302
- [class.flex-col]="field().optionsLayout !== 'grid'"
303
- role="radiogroup"
304
- [attr.aria-labelledby]="controlId"
305
- >
306
- @for (opt of resolvedOptions(); track opt.value) {
307
- <label class="inline-flex items-center gap-sm text-body-sm text-fg">
308
- <input
309
- type="radio"
310
- dgaRadio
311
- [name]="field().name"
312
- [value]="opt.value"
313
- [formControl]="control"
314
- [dgaInvalid]="!!error()"
315
- />
316
- {{ optionLabel(opt) }}
317
- </label>
302
+ <div class="flex flex-col gap-lg" role="radiogroup" [attr.aria-labelledby]="controlId">
303
+ @for (group of optionGroups(); track $index) {
304
+ @if (group.label) {
305
+ <p class="m-0 text-label-sm font-medium text-muted">{{ group.label }}</p>
306
+ }
307
+ <div [class]="optionsContainerClass()">
308
+ @for (opt of group.options; track opt.value) {
309
+ <label [class]="choiceLabelClass(opt)">
310
+ <input
311
+ type="radio"
312
+ dgaRadio
313
+ class="peer mt-xxs"
314
+ [name]="field().name"
315
+ [value]="opt.value"
316
+ [formControl]="control"
317
+ [disabled]="!!opt.disabled || !!field().disabled"
318
+ [dgaInvalid]="!!error()"
319
+ />
320
+ <span class="flex min-w-0 flex-col gap-xxs">
321
+ <span class="text-body-sm font-medium text-fg">{{ optionLabel(opt) }}</span>
322
+ @if (optionDescription(opt); as desc) {
323
+ <span class="text-body-xs text-muted">{{ desc }}</span>
324
+ }
325
+ </span>
326
+ @if (isCardsLayout()) {
327
+ <span
328
+ class="ms-auto hidden shrink-0 text-fg-primary peer-checked:inline-flex"
329
+ >
330
+ <dga-icon name="checkCircle" size="sm" tone="primary" />
331
+ </span>
332
+ }
333
+ </label>
334
+ }
335
+ </div>
318
336
  }
319
337
  </div>
320
338
  }
321
339
  @case ('checkbox') {
322
- <div
323
- class="flex flex-wrap gap-lg"
324
- [class.flex-col]="field().optionsLayout !== 'grid'"
325
- >
326
- @for (opt of resolvedOptions(); track opt.value) {
327
- <label class="inline-flex items-center gap-sm text-body-sm text-fg">
328
- <input
329
- type="checkbox"
330
- dgaCheckbox
331
- [checked]="isChecked(opt.value)"
332
- [disabled]="!!field().disabled"
333
- [dgaInvalid]="!!error()"
334
- (change)="toggleCheckbox(opt.value, $any($event.target).checked)"
335
- />
336
- {{ optionLabel(opt) }}
337
- </label>
340
+ <div class="flex flex-col gap-lg">
341
+ @for (group of optionGroups(); track $index) {
342
+ @if (group.label) {
343
+ <p class="m-0 text-label-sm font-medium text-muted">{{ group.label }}</p>
344
+ }
345
+ <div [class]="optionsContainerClass()">
346
+ @for (opt of group.options; track opt.value) {
347
+ <label [class]="choiceLabelClass(opt)">
348
+ <input
349
+ type="checkbox"
350
+ dgaCheckbox
351
+ class="peer mt-xxs"
352
+ [checked]="isChecked(opt.value)"
353
+ [disabled]="!!opt.disabled || !!field().disabled"
354
+ [dgaInvalid]="!!error()"
355
+ (change)="toggleCheckbox(opt.value, $any($event.target).checked)"
356
+ />
357
+ <span class="flex min-w-0 flex-col gap-xxs">
358
+ <span class="text-body-sm font-medium text-fg">{{ optionLabel(opt) }}</span>
359
+ @if (optionDescription(opt); as desc) {
360
+ <span class="text-body-xs text-muted">{{ desc }}</span>
361
+ }
362
+ </span>
363
+ @if (isCardsLayout()) {
364
+ <span
365
+ class="ms-auto hidden shrink-0 text-fg-primary peer-checked:inline-flex"
366
+ >
367
+ <dga-icon name="checkCircle" size="sm" tone="primary" />
368
+ </span>
369
+ }
370
+ </label>
371
+ }
372
+ </div>
338
373
  }
339
374
  </div>
340
375
  }
@@ -367,7 +402,7 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
367
402
  }
368
403
  </dga-field>
369
404
  }
370
- `,isInline:!0,dependencies:[{kind:"ngmodule",type:O},{kind:"directive",type:h.DefaultValueAccessor,selector:"input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"},{kind:"directive",type:h.RadioControlValueAccessor,selector:"input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]",inputs:["name","formControlName","value"]},{kind:"directive",type:h.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:h.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:h.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"ngmodule",type:Z},{kind:"component",type:ie,selector:"dga-field",inputs:["label","labelHint","hint","error","status","statusMessage","controlId","required"]},{kind:"directive",type:ne,selector:"input[dgaInput], textarea[dgaInput]",inputs:["size","dgaInvalid","status"]},{kind:"component",type:oe,selector:"dga-select",inputs:["options","value","multiple","searchable","openMode","clearable","size","status","placeholder","label","removeLabel","clearLabel","emptyLabel","disabled","dgaInvalid"],outputs:["valueChange","searchChange"]},{kind:"directive",type:re,selector:'input[type="checkbox"][dgaCheckbox]',inputs:["size","dgaInvalid"]},{kind:"directive",type:le,selector:'input[type="radio"][dgaRadio]',inputs:["size","dgaInvalid"]},{kind:"component",type:se,selector:"dga-phone-input",inputs:["countries","countryCode","nationalNumber","controlId","name","placeholder","autocomplete","codeLabel","disabled","required","invalid","value"],outputs:["countryCodeChange","nationalNumberChange","valueChange"]},{kind:"component",type:he,selector:"dga-date-picker",inputs:["value","startValue","endValue","calendar","range","format","mode","locale","minDate","maxDate","yearBefore","yearAfter","placeholder","controlId","name","size","disabled","required","clearable","editable","dgaInvalid","status","rangeSeparator","todayLabel","clearLabel","closeLabel","saveLabel","ariaLabel"],outputs:["startValueChange","endValueChange","valueChange","rangeChange"]},{kind:"component",type:de,selector:"dga-chip-input",inputs:["value","placeholder","label","removeLabel","disabled","allowDuplicates","dgaInvalid","status"],outputs:["valueChange"]},{kind:"component",type:ue,selector:"dga-switch",inputs:["checked","size","label","disabled"],outputs:["checkedChange","changed"]},{kind:"component",type:ce,selector:"dga-upload",inputs:["files","accept","multiple","appearance","hint","browseLabel","removeLabel","disabled","maxSize","maxFiles","dragging"],outputs:["filesChange","rejected","draggingChange"]},{kind:"component",type:pe,selector:"dga-number-input",inputs:["value","min","max","step","size","label","controlId","name","incrementLabel","decrementLabel","disabled","dgaInvalid","status"],outputs:["valueChange"]},{kind:"component",type:fe,selector:"dga-otp",inputs:["length","value","label","alphanumeric","disabled","dgaInvalid"],outputs:["valueChange","completed"]},{kind:"component",type:v,selector:"dga-dynamic-form-staged-upload",inputs:["accept","maxFileSize","maxFiles","uploadEndpoint","deleteEndpoint","deleteMethod","hint","browseLabel","removeLabel","downloadLabel","uploadingLabel","errorLabel","disabled"],outputs:["fileDownload"]}],changeDetection:l.ChangeDetectionStrategy.OnPush})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:D,decorators:[{type:R,args:[{selector:"dga-dynamic-form-field",standalone:!0,imports:[O,Z,ie,ne,oe,re,le,se,he,de,ue,ce,pe,fe,v],template:`
405
+ `,isInline:!0,dependencies:[{kind:"ngmodule",type:O},{kind:"directive",type:b.DefaultValueAccessor,selector:"input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"},{kind:"directive",type:b.RadioControlValueAccessor,selector:"input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]",inputs:["name","formControlName","value"]},{kind:"directive",type:b.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:b.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:b.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"ngmodule",type:ie},{kind:"component",type:re,selector:"dga-field",inputs:["label","labelHint","hint","error","status","statusMessage","controlId","required"]},{kind:"directive",type:le,selector:"input[dgaInput], textarea[dgaInput]",inputs:["size","dgaInvalid","status"]},{kind:"component",type:se,selector:"dga-select",inputs:["options","value","multiple","searchable","openMode","clearable","size","status","placeholder","label","removeLabel","clearLabel","emptyLabel","disabled","dgaInvalid"],outputs:["valueChange","searchChange"]},{kind:"directive",type:de,selector:'input[type="checkbox"][dgaCheckbox]',inputs:["size","dgaInvalid"]},{kind:"directive",type:ce,selector:'input[type="radio"][dgaRadio]',inputs:["size","dgaInvalid"]},{kind:"component",type:ue,selector:"dga-phone-input",inputs:["countries","locale","countryCode","nationalNumber","controlId","name","placeholder","autocomplete","codeLabel","disabled","required","invalid","value"],outputs:["countryCodeChange","nationalNumberChange","valueChange"]},{kind:"component",type:Se,selector:"dga-date-picker",inputs:["value","startValue","endValue","calendar","range","format","mode","locale","minDate","maxDate","yearBefore","yearAfter","placeholder","controlId","name","size","disabled","required","clearable","editable","dgaInvalid","status","rangeSeparator","todayLabel","clearLabel","closeLabel","saveLabel","ariaLabel"],outputs:["startValueChange","endValueChange","valueChange","rangeChange"]},{kind:"component",type:pe,selector:"dga-chip-input",inputs:["value","placeholder","label","removeLabel","disabled","allowDuplicates","dgaInvalid","status"],outputs:["valueChange"]},{kind:"component",type:fe,selector:"dga-switch",inputs:["checked","size","label","disabled"],outputs:["checkedChange","changed"]},{kind:"component",type:me,selector:"dga-upload",inputs:["files","accept","multiple","appearance","hint","browseLabel","removeLabel","disabled","maxSize","maxFiles","dragging"],outputs:["filesChange","rejected","draggingChange"]},{kind:"component",type:ge,selector:"dga-number-input",inputs:["value","min","max","step","size","label","controlId","name","incrementLabel","decrementLabel","disabled","dgaInvalid","status"],outputs:["valueChange"]},{kind:"component",type:he,selector:"dga-otp",inputs:["length","value","label","alphanumeric","disabled","dgaInvalid"],outputs:["valueChange","completed"]},{kind:"component",type:C,selector:"dga-icon",inputs:["name","glyph","size","tone","label"]},{kind:"component",type:y,selector:"dga-dynamic-form-staged-upload",inputs:["accept","maxFileSize","maxFiles","uploadEndpoint","deleteEndpoint","deleteMethod","hint","browseLabel","removeLabel","downloadLabel","uploadingLabel","errorLabel","disabled"],outputs:["fileDownload"]}],changeDetection:l.ChangeDetectionStrategy.OnPush})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:w,decorators:[{type:V,args:[{selector:"dga-dynamic-form-field",standalone:!0,imports:[O,ie,re,le,se,de,ce,ue,Se,pe,fe,me,ge,he,C,y],template:`
371
406
  @if (field().type !== 'hidden') {
372
407
  <dga-field
373
408
  [label]="labelText()"
@@ -420,6 +455,8 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
420
455
  [disabled]="!!field().disabled"
421
456
  [required]="required()"
422
457
  [controlId]="controlId"
458
+ [locale]="locale()"
459
+ [countries]="phoneCountries()"
423
460
  />
424
461
  }
425
462
  @case ('date') {
@@ -510,44 +547,77 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
510
547
  />
511
548
  }
512
549
  @case ('radio') {
513
- <div
514
- class="flex flex-wrap gap-lg"
515
- [class.flex-col]="field().optionsLayout !== 'grid'"
516
- role="radiogroup"
517
- [attr.aria-labelledby]="controlId"
518
- >
519
- @for (opt of resolvedOptions(); track opt.value) {
520
- <label class="inline-flex items-center gap-sm text-body-sm text-fg">
521
- <input
522
- type="radio"
523
- dgaRadio
524
- [name]="field().name"
525
- [value]="opt.value"
526
- [formControl]="control"
527
- [dgaInvalid]="!!error()"
528
- />
529
- {{ optionLabel(opt) }}
530
- </label>
550
+ <div class="flex flex-col gap-lg" role="radiogroup" [attr.aria-labelledby]="controlId">
551
+ @for (group of optionGroups(); track $index) {
552
+ @if (group.label) {
553
+ <p class="m-0 text-label-sm font-medium text-muted">{{ group.label }}</p>
554
+ }
555
+ <div [class]="optionsContainerClass()">
556
+ @for (opt of group.options; track opt.value) {
557
+ <label [class]="choiceLabelClass(opt)">
558
+ <input
559
+ type="radio"
560
+ dgaRadio
561
+ class="peer mt-xxs"
562
+ [name]="field().name"
563
+ [value]="opt.value"
564
+ [formControl]="control"
565
+ [disabled]="!!opt.disabled || !!field().disabled"
566
+ [dgaInvalid]="!!error()"
567
+ />
568
+ <span class="flex min-w-0 flex-col gap-xxs">
569
+ <span class="text-body-sm font-medium text-fg">{{ optionLabel(opt) }}</span>
570
+ @if (optionDescription(opt); as desc) {
571
+ <span class="text-body-xs text-muted">{{ desc }}</span>
572
+ }
573
+ </span>
574
+ @if (isCardsLayout()) {
575
+ <span
576
+ class="ms-auto hidden shrink-0 text-fg-primary peer-checked:inline-flex"
577
+ >
578
+ <dga-icon name="checkCircle" size="sm" tone="primary" />
579
+ </span>
580
+ }
581
+ </label>
582
+ }
583
+ </div>
531
584
  }
532
585
  </div>
533
586
  }
534
587
  @case ('checkbox') {
535
- <div
536
- class="flex flex-wrap gap-lg"
537
- [class.flex-col]="field().optionsLayout !== 'grid'"
538
- >
539
- @for (opt of resolvedOptions(); track opt.value) {
540
- <label class="inline-flex items-center gap-sm text-body-sm text-fg">
541
- <input
542
- type="checkbox"
543
- dgaCheckbox
544
- [checked]="isChecked(opt.value)"
545
- [disabled]="!!field().disabled"
546
- [dgaInvalid]="!!error()"
547
- (change)="toggleCheckbox(opt.value, $any($event.target).checked)"
548
- />
549
- {{ optionLabel(opt) }}
550
- </label>
588
+ <div class="flex flex-col gap-lg">
589
+ @for (group of optionGroups(); track $index) {
590
+ @if (group.label) {
591
+ <p class="m-0 text-label-sm font-medium text-muted">{{ group.label }}</p>
592
+ }
593
+ <div [class]="optionsContainerClass()">
594
+ @for (opt of group.options; track opt.value) {
595
+ <label [class]="choiceLabelClass(opt)">
596
+ <input
597
+ type="checkbox"
598
+ dgaCheckbox
599
+ class="peer mt-xxs"
600
+ [checked]="isChecked(opt.value)"
601
+ [disabled]="!!opt.disabled || !!field().disabled"
602
+ [dgaInvalid]="!!error()"
603
+ (change)="toggleCheckbox(opt.value, $any($event.target).checked)"
604
+ />
605
+ <span class="flex min-w-0 flex-col gap-xxs">
606
+ <span class="text-body-sm font-medium text-fg">{{ optionLabel(opt) }}</span>
607
+ @if (optionDescription(opt); as desc) {
608
+ <span class="text-body-xs text-muted">{{ desc }}</span>
609
+ }
610
+ </span>
611
+ @if (isCardsLayout()) {
612
+ <span
613
+ class="ms-auto hidden shrink-0 text-fg-primary peer-checked:inline-flex"
614
+ >
615
+ <dga-icon name="checkCircle" size="sm" tone="primary" />
616
+ </span>
617
+ }
618
+ </label>
619
+ }
620
+ </div>
551
621
  }
552
622
  </div>
553
623
  }
@@ -580,21 +650,107 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
580
650
  }
581
651
  </dga-field>
582
652
  }
583
- `,changeDetection:V.OnPush,host:{class:"block w-full","[class.hidden]":'field().type === "hidden"'}}]}],propDecorators:{field:[{type:l.Input,args:[{isSignal:!0,alias:"field",required:!0}]}],form:[{type:l.Input,args:[{isSignal:!0,alias:"form",required:!0}]}],locale:[{type:l.Input,args:[{isSignal:!0,alias:"locale",required:!1}]}],required:[{type:l.Input,args:[{isSignal:!0,alias:"required",required:!1}]}],error:[{type:l.Input,args:[{isSignal:!0,alias:"error",required:!1}]}],runtimeOptions:[{type:l.Input,args:[{isSignal:!0,alias:"runtimeOptions",required:!1}]}],consentBaseUrl:[{type:l.Input,args:[{isSignal:!0,alias:"consentBaseUrl",required:!1}]}],optionsLoaded:[{type:l.Output,args:["optionsLoaded"]}],fileDownload:[{type:l.Output,args:["fileDownload"]}]}});const ct={1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12"},pt={1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12"},ft={1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12"};function H(i){return Number.isFinite(i)?Math.min(12,Math.max(1,Math.round(i))):12}class N{config=u.required(...ngDevMode?[{debugName:"config"}]:[]);localeInput=u(null,{...ngDevMode?{debugName:"localeInput"}:{},alias:"locale"});formSubmitted=g();formError=g();clearButtonClick=g();draftSaved=g();fileDownload=g();fb=c(_e);formService=c(C);submitAdapter=c(E,{optional:!0});toast=c(B,{optional:!0});i18n=c(U,{optional:!0});destroyRef=c(Q);registry=c(L);form;formSignature="";valueChangesSub=null;activeStep=m(0,...ngDevMode?[{debugName:"activeStep"}]:[]);submitting=m(!1,...ngDevMode?[{debugName:"submitting"}]:[]);optionsMap=m({},...ngDevMode?[{debugName:"optionsMap"}]:[]);formValues=m({},...ngDevMode?[{debugName:"formValues"}]:[]);locale=p(()=>this.localeInput()??Te(),...ngDevMode?[{debugName:"locale"}]:[]);wizardEnabled=p(()=>!!this.config().wizard?.enabled&&!!this.config().wizard?.steps?.length,...ngDevMode?[{debugName:"wizardEnabled"}]:[]);allFields=p(()=>Le(this.config().fields,this.config().wizard?.steps),...ngDevMode?[{debugName:"allFields"}]:[]);currentStepFields=p(()=>this.wizardEnabled()?this.config().wizard.steps[this.activeStep()]?.fields??[]:this.config().fields,...ngDevMode?[{debugName:"currentStepFields"}]:[]);visibleFields=p(()=>{const e=this.formValues(),t=this.optionsMap(),a=this.allFields(),n=o=>a.find(r=>r.name===o);return this.currentStepFields().filter(o=>W(o,e,r=>t[r],n))},...ngDevMode?[{debugName:"visibleFields"}]:[]);stepperSteps=p(()=>{const e=this.config().wizard?.steps??[],t=this.activeStep(),a=!!this.config().wizard?.allowSkipSteps;return e.map((n,o)=>({label:this.labelOf(n.title),description:n.description?this.labelOf(n.description):void 0,disabled:!a&&o>t}))},...ngDevMode?[{debugName:"stepperSteps"}]:[]);descriptionText=p(()=>this.labelOf(this.config().description),...ngDevMode?[{debugName:"descriptionText"}]:[]);constructor(){this.form=this.fb.group({}),this.destroyRef.onDestroy(()=>this.valueChangesSub?.unsubscribe()),k(()=>{const e=this.allFields(),t=e.map(n=>`${n.name}:${n.type}`).join("|"),a=this.config();q(()=>{if(t===this.formSignature)return;const n=Object.keys(this.form.controls).length?this.form.getRawValue():null,o=this.formSignature==="";this.formSignature=t;const r=Ce(this.fb,e);if(n)r.patchValue(n,{emitEvent:!1});else if(o&&a.formId&&a.enableLocalStorageSave){const f=this.formService.loadDraft(a.formId);f&&r.patchValue(f,{emitEvent:!1})}this.form=r,De(r,e);const s=r.get("password"),d=r.get("confirmPassword");s&&d&&s.valueChanges.pipe(T(this.destroyRef)).subscribe(()=>d.updateValueAndValidity({emitEvent:!1})),o||this.activeStep.set(0),this.formValues.set(r.getRawValue()),this.valueChangesSub?.unsubscribe(),this.valueChangesSub=r.valueChanges.pipe(Ge(r.getRawValue())).subscribe(f=>this.formValues.set(f))})}),k(()=>{const e=this.formValues(),t=this.optionsMap();if(this.form)for(const a of this.allFields()){const n=this.form.get(a.name);if(!n||!a.requiredWhen)continue;const o=this.allFields(),s=K(a,e,d=>t[d],d=>o.find(f=>f.name===d));Fe(n,a,s)}}),k(()=>{const e=this.allFields();q(()=>{if(!this.form)return;const t={};for(const a of e)a.value!==void 0&&(t[a.name]=a.value);Object.keys(t).length&&this.form.patchValue(t,{emitEvent:!1})})}),k(()=>{const e=this.activeStep(),t=this.config().wizard;q(()=>{!t?.steps?.[e]?.initialData||!this.form||this.form.patchValue(t.steps[e].initialData,{emitEvent:!1})})})}labelOf(e){return e?this.i18n?this.i18n.resolveLabel(e,this.locale()):I(e,this.locale()):""}submitLabel(){const e=this.config();return this.wizardEnabled()?this.labelOf(e.wizard?.submitButtonText??e.submitButtonLabel)||(this.locale()==="en"?"Submit":"إرسال"):this.labelOf(e.submitButtonLabel)||(this.locale()==="en"?"Submit":"إرسال")}nextLabel(){return this.labelOf(this.config().wizard?.nextButtonText)||(this.locale()==="en"?"Next":"التالي")}previousLabel(){return this.labelOf(this.config().wizard?.previousButtonText)||(this.locale()==="en"?"Previous":"السابق")}clearLabel(){return this.labelOf(this.config().clearButtonLabel)}saveLabel(){return this.labelOf(this.config().saveButtonLabel??this.config().wizard?.saveButtonText)||(this.locale()==="en"?"Save":"حفظ")}showSave(){const e=this.config();return!!(e.enableLocalStorageSave||e.wizard?.showSaveButton)}isLastStep(){return this.wizardEnabled()?this.activeStep()>=this.config().wizard.steps.length-1:!0}isRequired(e){const t=this.allFields(),a=n=>t.find(o=>o.name===n);return K(e,this.formValues(),n=>this.optionsMap()[n],a)}fieldError(e){const t=this.form?.get(e.name);if(!t||!(t.touched||t.dirty)||!t.errors)return"";const a=t.errors,n=e.validation?.errorMessages,o=this.locale(),r=s=>n?.[s]?this.labelOf(n[s]):"";return a.required?r("required")||(o==="en"?"Required":"مطلوب"):a.email?r("email")||(o==="en"?"Invalid email":"بريد غير صالح"):a.minlength?r("minLength")||(o==="en"?"Too short":"قصير جداً"):a.maxlength?r("maxLength")||(o==="en"?"Too long":"طويل جداً"):a.pattern?r("pattern")||(o==="en"?"Invalid format":"صيغة غير صالحة"):a.maxChips?r("maxChips")||(o==="en"?"Too many items":"عدد العناصر كبير"):a.invalidFormat?r("invalidFormat")||(o==="en"?"Invalid format":"صيغة غير صالحة"):a.invalidPrefix?r("invalidPrefix")||(o==="en"?"Invalid prefix":"بادئة غير صالحة"):a.invalidLength?r("invalidLength")||(o==="en"?"Invalid length":"طول غير صالح"):a.passwordMismatch?r("passwordMismatch")||(o==="en"?"Passwords do not match":"كلمات المرور غير متطابقة"):a.endDateAfterStartDate?r("endDateAfterStartDate")||(o==="en"?"End date must be after start date":"تاريخ النهاية يجب أن يكون بعد تاريخ البداية"):a.uploadInProgress?r("uploadInProgress")||(o==="en"?"Upload in progress":"جاري رفع الملفات"):a.maxFiles?r("maxFiles")||(o==="en"?"Too many files":"عدد الملفات كبير"):a.minDate?r("minDate")||(o==="en"?"Date is too early":"التاريخ مبكر جداً"):a.maxDate?r("maxDate")||(o==="en"?"Date is too late":"التاريخ متأخر جداً"):o==="en"?"Invalid":"غير صالح"}columnClass(e){const t=e.columns,a=H(t?.sm??t?.md??12),n=H(t?.md??a),o=H(t?.lg??n);return["w-full",ct[a],pt[n],ft[o]].join(" ")}customComponent(e){return this.registry.hasCustom(e.type)?this.registry.get(e.type):null}customInputs(e){return{field:e,form:this.form,locale:this.locale(),required:this.isRequired(e),error:this.fieldError(e)}}onOptionsLoaded(e){this.optionsMap.update(t=>({...t,[e.name]:e.options}))}previousStep(){this.activeStep()>0&&this.activeStep.update(e=>e-1)}canActivateStep=(e,t)=>{if(e<=t)return!0;const a=this.config().wizard;return!a?.allowSkipSteps&&e>t+1?!1:a?.validateOnStepChange===!1?!0:(this.markStepTouched(),this.stepValid())};nextStep(){this.config().wizard?.validateOnStepChange!==!1&&(this.markStepTouched(),!this.stepValid())||this.isLastStep()||(this.activeStep.update(t=>t+1),this.skipEmptyStepsForward())}skipEmptyStepsForward(){const e=this.config().wizard;if(e?.steps)for(;this.activeStep()<e.steps.length-1&&this.visibleFields().length===0;)this.activeStep.update(t=>t+1)}onClear(){this.form.reset();for(const t of this.allFields()){const a=this.form.get(t.name);a&&a.setValue(t.value!==void 0?t.value:["checkbox","multiselect","chips","file","multifile","multifilestaged"].includes(t.type)?[]:t.type==="toggle"?!1:"")}this.activeStep.set(0);const e=this.config().formId;e&&this.formService.clearDraft(e),this.clearButtonClick.emit()}onSaveDraft(){const e=this.form.getRawValue(),t=this.config(),a=t.formId;a&&this.formService.saveDraft(a,e,this.allFields());const n=t.wizard;if(n?.autoSave&&n.saveEndpoint&&this.submitAdapter){const o=this.formService.buildPayload(t,e,this.allFields());this.submitAdapter.submit({endpoint:n.saveEndpoint,method:n.saveMethod??"POST",body:o}).subscribe({error:r=>this.formError.emit(r)})}this.draftSaved.emit(e),this.toast?.show({variant:"success",message:this.locale()==="en"?"Draft saved":"تم حفظ المسودة"})}onSubmit(){if(this.wizardEnabled()&&!this.isLastStep()){this.nextStep();return}if(this.markVisibleTouched(),!this.visibleValid()){this.toast?.show({variant:"error",message:this.locale()==="en"?"Please fix the highlighted fields":"يرجى تصحيح الحقول المحددة"});return}const e=this.config(),t=this.form.getRawValue(),a=new Set(this.collectVisibleFieldNames()),n={};for(const o of this.allFields())!a.has(o.name)&&o.type!=="hidden"||(n[o.name]=t[o.name]);if(e.emitOnly||!e.endpoint){this.formSubmitted.emit(n),this.toast?.show({variant:"success",message:this.labelOf(e.successMessage)||(this.locale()==="en"?"Submitted":"تم الإرسال")});return}this.submitting.set(!0),this.formService.submitForm(e,n,this.allFields()).subscribe({next:o=>{this.submitting.set(!1),this.formSubmitted.emit(o),e.formId&&this.formService.clearDraft(e.formId),this.toast?.show({variant:"success",message:this.labelOf(e.successMessage)||(this.locale()==="en"?"Submitted successfully":"تم الإرسال بنجاح")})},error:o=>{this.submitting.set(!1),this.formError.emit(o),this.toast?.show({variant:"error",message:this.labelOf(e.errorMessage)||(this.locale()==="en"?"Submission failed":"فشل الإرسال")})}})}patchFormValues(e){this.form.patchValue(e)}getForm(){return this.form}markStepTouched(){for(const e of this.visibleFields())this.form.get(e.name)?.markAsTouched()}markVisibleTouched(){for(const e of this.collectVisibleFieldNames())this.form.get(e)?.markAsTouched()}stepValid(){return this.config().wizard?.steps[this.activeStep()]?.optional?!0:this.visibleFields().every(t=>{const a=this.form.get(t.name);return!a||a.disabled||a.valid})}visibleValid(){return this.collectVisibleFieldNames().every(e=>{const t=this.form.get(e);return!t||t.disabled||t.valid})}collectVisibleFieldNames(){const e=this.formValues(),t=this.optionsMap(),a=this.wizardEnabled()?this.allFields():this.config().fields,n=o=>a.find(r=>r.name===o);return a.filter(o=>o.type==="hidden"||W(o,e,r=>t[r],n)).map(o=>o.name)}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:N,deps:[],target:l.ɵɵFactoryTarget.Component});static ɵcmp=l.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"22.0.7",type:N,isStandalone:!0,selector:"dga-dynamic-form",inputs:{config:{classPropertyName:"config",publicName:"config",isSignal:!0,isRequired:!0,transformFunction:null},localeInput:{classPropertyName:"localeInput",publicName:"locale",isSignal:!0,isRequired:!1,transformFunction:null}},outputs:{formSubmitted:"formSubmitted",formError:"formError",clearButtonClick:"clearButtonClick",draftSaved:"draftSaved",fileDownload:"fileDownload"},host:{classAttribute:"block w-full"},ngImport:l,template:`
584
- <form class="flex w-full flex-col gap-3xl" [formGroup]="form" (ngSubmit)="onSubmit()">
653
+ `,changeDetection:R.OnPush,host:{class:"block w-full","[class.hidden]":'field().type === "hidden"'}}]}],propDecorators:{field:[{type:l.Input,args:[{isSignal:!0,alias:"field",required:!0}]}],form:[{type:l.Input,args:[{isSignal:!0,alias:"form",required:!0}]}],locale:[{type:l.Input,args:[{isSignal:!0,alias:"locale",required:!1}]}],required:[{type:l.Input,args:[{isSignal:!0,alias:"required",required:!1}]}],error:[{type:l.Input,args:[{isSignal:!0,alias:"error",required:!1}]}],runtimeOptions:[{type:l.Input,args:[{isSignal:!0,alias:"runtimeOptions",required:!1}]}],consentBaseUrl:[{type:l.Input,args:[{isSignal:!0,alias:"consentBaseUrl",required:!1}]}],optionsLoaded:[{type:l.Output,args:["optionsLoaded"]}],fileDownload:[{type:l.Output,args:["fileDownload"]}]}});const vt={1:"col-span-1",2:"col-span-2",3:"col-span-3",4:"col-span-4",5:"col-span-5",6:"col-span-6",7:"col-span-7",8:"col-span-8",9:"col-span-9",10:"col-span-10",11:"col-span-11",12:"col-span-12"},yt={1:"md:col-span-1",2:"md:col-span-2",3:"md:col-span-3",4:"md:col-span-4",5:"md:col-span-5",6:"md:col-span-6",7:"md:col-span-7",8:"md:col-span-8",9:"md:col-span-9",10:"md:col-span-10",11:"md:col-span-11",12:"md:col-span-12"},xt={1:"lg:col-span-1",2:"lg:col-span-2",3:"lg:col-span-3",4:"lg:col-span-4",5:"lg:col-span-5",6:"lg:col-span-6",7:"lg:col-span-7",8:"lg:col-span-8",9:"lg:col-span-9",10:"lg:col-span-10",11:"lg:col-span-11",12:"lg:col-span-12"};function Q(i){return Number.isFinite(i)?Math.min(12,Math.max(1,Math.round(i))):12}class F{config=c.required(...ngDevMode?[{debugName:"config"}]:[]);localeInput=c(null,{...ngDevMode?{debugName:"localeInput"}:{},alias:"locale"});formSubmitted=h();formError=h();clearButtonClick=h();draftSaved=h();fileDownload=h();fb=p(Ke);formService=p(I);submitAdapter=p(M,{optional:!0});toast=p($,{optional:!0});i18n=p(G,{optional:!0});destroyRef=p(ee);registry=p(k);form;formSignature="";valueChangesSub=null;activeStep=f(0,...ngDevMode?[{debugName:"activeStep"}]:[]);submitting=f(!1,...ngDevMode?[{debugName:"submitting"}]:[]);draftRestored=f(!1,...ngDevMode?[{debugName:"draftRestored"}]:[]);draftJustSaved=f(!1,...ngDevMode?[{debugName:"draftJustSaved"}]:[]);optionsMap=f({},...ngDevMode?[{debugName:"optionsMap"}]:[]);formValues=f({},...ngDevMode?[{debugName:"formValues"}]:[]);draftSavedTimer=null;locale=u(()=>this.localeInput()??Re(),...ngDevMode?[{debugName:"locale"}]:[]);wizardEnabled=u(()=>!!this.config().wizard?.enabled&&!!this.config().wizard?.steps?.length,...ngDevMode?[{debugName:"wizardEnabled"}]:[]);allFields=u(()=>Ee(this.config().fields,this.config().wizard?.steps),...ngDevMode?[{debugName:"allFields"}]:[]);currentStepFields=u(()=>this.wizardEnabled()?this.config().wizard.steps[this.activeStep()]?.fields??[]:this.config().fields,...ngDevMode?[{debugName:"currentStepFields"}]:[]);visibleFields=u(()=>{const e=this.formValues(),t=this.optionsMap(),a=this.allFields(),n=o=>a.find(r=>r.name===o);return this.currentStepFields().filter(o=>Y(o,e,r=>t[r],n))},...ngDevMode?[{debugName:"visibleFields"}]:[]);stepperSteps=u(()=>{const e=this.config().wizard?.steps??[],t=this.activeStep(),a=!!this.config().wizard?.allowSkipSteps;return e.map((n,o)=>({label:this.labelOf(n.title),disabled:!a&&o>t}))},...ngDevMode?[{debugName:"stepperSteps"}]:[]);descriptionText=u(()=>this.labelOf(this.config().description),...ngDevMode?[{debugName:"descriptionText"}]:[]);currentStep=u(()=>this.wizardEnabled()?this.config().wizard?.steps[this.activeStep()]:void 0,...ngDevMode?[{debugName:"currentStep"}]:[]);currentStepTitle=u(()=>this.labelOf(this.currentStep()?.title),...ngDevMode?[{debugName:"currentStepTitle"}]:[]);currentStepDescription=u(()=>this.labelOf(this.currentStep()?.description),...ngDevMode?[{debugName:"currentStepDescription"}]:[]);currentStepOptional=u(()=>!!this.currentStep()?.optional,...ngDevMode?[{debugName:"currentStepOptional"}]:[]);stepCount=u(()=>this.config().wizard?.steps.length??0,...ngDevMode?[{debugName:"stepCount"}]:[]);stepPercent=u(()=>{const e=this.stepCount();return e?Math.round((this.activeStep()+1)/e*100):0},...ngDevMode?[{debugName:"stepPercent"}]:[]);stepFraction(){return`${this.activeStep()+1}/${this.stepCount()}`}constructor(){this.form=this.fb.group({}),this.destroyRef.onDestroy(()=>{this.valueChangesSub?.unsubscribe(),this.draftSavedTimer&&clearTimeout(this.draftSavedTimer)}),N(()=>{const e=this.allFields(),t=e.map(n=>`${n.name}:${n.type}`).join("|"),a=this.config();q(()=>{if(t===this.formSignature)return;const n=Object.keys(this.form.controls).length?this.form.getRawValue():null,o=this.formSignature==="";this.formSignature=t;const r=Ae(this.fb,e);if(n)r.patchValue(n,{emitEvent:!1});else if(o&&a.formId&&a.enableLocalStorageSave){const g=this.formService.loadDraft(a.formId);g&&(r.patchValue(g,{emitEvent:!1}),this.draftRestored.set(!0))}this.form=r,Te(r,e);const s=r.get("password"),d=r.get("confirmPassword");s&&d&&s.valueChanges.pipe(A(this.destroyRef)).subscribe(()=>d.updateValueAndValidity({emitEvent:!1})),o||this.activeStep.set(0),this.formValues.set(r.getRawValue()),this.valueChangesSub?.unsubscribe(),this.valueChangesSub=r.valueChanges.pipe(Je(r.getRawValue())).subscribe(g=>this.formValues.set(g))})}),N(()=>{const e=this.formValues(),t=this.optionsMap();if(this.form)for(const a of this.allFields()){const n=this.form.get(a.name);if(n)if(a.requiredWhen){const o=this.allFields(),s=J(a,e,d=>t[d],d=>o.find(g=>g.name===d));K(n,a,s)}else a.type==="phone"&&K(n,a,!!(a.required||a.validation?.required))}}),N(()=>{const e=this.allFields();q(()=>{if(!this.form)return;const t={};for(const a of e)a.value!==void 0&&(t[a.name]=a.value);Object.keys(t).length&&this.form.patchValue(t,{emitEvent:!1})})}),N(()=>{const e=this.activeStep(),t=this.config().wizard;q(()=>{!t?.steps?.[e]?.initialData||!this.form||this.form.patchValue(t.steps[e].initialData,{emitEvent:!1})})})}labelOf(e){return e?this.i18n?this.i18n.resolveLabel(e,this.locale()):m(e,this.locale()):""}submitLabel(){const e=this.config();return this.wizardEnabled()?this.labelOf(e.wizard?.submitButtonText??e.submitButtonLabel)||(this.locale()==="en"?"Submit":"إرسال"):this.labelOf(e.submitButtonLabel)||(this.locale()==="en"?"Submit":"إرسال")}nextLabel(){return this.labelOf(this.config().wizard?.nextButtonText)||(this.locale()==="en"?"Next":"التالي")}previousLabel(){return this.labelOf(this.config().wizard?.previousButtonText)||(this.locale()==="en"?"Back":"رجوع")}clearLabel(){return this.labelOf(this.config().clearButtonLabel)}saveLabel(){return this.labelOf(this.config().saveButtonLabel??this.config().wizard?.saveButtonText)||(this.locale()==="en"?"Save as draft":"حفظ كمسودة")}draftSavedLabel(){return this.locale()==="en"?"Draft saved":"تم حفظ المسودة"}draftRestoredTitle(){return this.locale()==="en"?"Draft restored":"تم استعادة المسودة"}draftRestoredBody(){return this.locale()==="en"?"Your previous answers were loaded. You can continue or discard the draft.":"تم تحميل إجاباتك السابقة. يمكنك المتابعة أو حذف المسودة."}discardDraftLabel(){return this.locale()==="en"?"Discard draft":"حذف المسودة"}optionalBadgeLabel(){return this.locale()==="en"?"Optional":"اختياري"}stepProgressLabel(){const e=this.stepCount(),t=this.activeStep()+1;return this.locale()==="en"?`Step ${t} of ${e}`:`الخطوة ${t} من ${e}`}showSave(){const e=this.config();return!!(e.enableLocalStorageSave||e.wizard?.showSaveButton)}isLastStep(){return this.wizardEnabled()?this.activeStep()>=this.config().wizard.steps.length-1:!0}isRequired(e){const t=this.allFields(),a=n=>t.find(o=>o.name===n);return J(e,this.formValues(),n=>this.optionsMap()[n],a)}fieldError(e){const t=this.form?.get(e.name);if(!t||!(t.touched||t.dirty)||!t.errors)return"";const a=t.errors,n=e.validation?.errorMessages,o=this.locale(),r=s=>n?.[s]?this.labelOf(n[s]):"";return a.required?r("required")||(o==="en"?"Required":"مطلوب"):a.email?r("email")||(o==="en"?"Invalid email":"بريد غير صالح"):a.minlength?r("minLength")||(o==="en"?"Too short":"قصير جداً"):a.maxlength?r("maxLength")||(o==="en"?"Too long":"طويل جداً"):a.pattern?r("pattern")||(o==="en"?"Invalid format":"صيغة غير صالحة"):a.maxChips?r("maxChips")||(o==="en"?"Too many items":"عدد العناصر كبير"):a.invalidFormat?r("invalidFormat")||(o==="en"?"Invalid format":"صيغة غير صالحة"):a.invalidPrefix?r("invalidPrefix")||(o==="en"?"Invalid prefix":"بادئة غير صالحة"):a.invalidLength?r("invalidLength")||(o==="en"?"Invalid length":"طول غير صالح"):a.passwordMismatch?r("passwordMismatch")||(o==="en"?"Passwords do not match":"كلمات المرور غير متطابقة"):a.endDateAfterStartDate?r("endDateAfterStartDate")||(o==="en"?"End date must be after start date":"تاريخ النهاية يجب أن يكون بعد تاريخ البداية"):a.uploadInProgress?r("uploadInProgress")||(o==="en"?"Upload in progress":"جاري رفع الملفات"):a.maxFiles?r("maxFiles")||(o==="en"?"Too many files":"عدد الملفات كبير"):a.minDate?r("minDate")||(o==="en"?"Date is too early":"التاريخ مبكر جداً"):a.maxDate?r("maxDate")||(o==="en"?"Date is too late":"التاريخ متأخر جداً"):o==="en"?"Invalid":"غير صالح"}columnClass(e){const t=e.columns,a=Q(t?.sm??t?.md??12),n=Q(t?.md??a),o=Q(t?.lg??n);return["w-full",vt[a],yt[n],xt[o]].join(" ")}customComponent(e){return this.registry.hasCustom(e.type)?this.registry.get(e.type):null}customInputs(e){return{field:e,form:this.form,locale:this.locale(),required:this.isRequired(e),error:this.fieldError(e)}}onOptionsLoaded(e){this.optionsMap.update(t=>({...t,[e.name]:e.options}))}previousStep(){this.activeStep()>0&&this.activeStep.update(e=>e-1)}canActivateStep=(e,t)=>{if(e<=t)return!0;const a=this.config().wizard;return!a?.allowSkipSteps&&e>t+1?!1:a?.validateOnStepChange===!1?!0:(this.markStepTouched(),this.stepValid())};nextStep(){this.config().wizard?.validateOnStepChange!==!1&&(this.markStepTouched(),!this.stepValid())||this.isLastStep()||(this.activeStep.update(t=>t+1),this.skipEmptyStepsForward())}skipEmptyStepsForward(){const e=this.config().wizard;if(e?.steps)for(;this.activeStep()<e.steps.length-1&&this.visibleFields().length===0;)this.activeStep.update(t=>t+1)}onClear(){this.form.reset();for(const t of this.allFields()){const a=this.form.get(t.name);a&&a.setValue(t.value!==void 0?t.value:["checkbox","multiselect","chips","file","multifile","multifilestaged"].includes(t.type)?[]:t.type==="toggle"?!1:"")}this.activeStep.set(0),this.draftRestored.set(!1),this.draftJustSaved.set(!1);const e=this.config().formId;e&&this.formService.clearDraft(e),this.clearButtonClick.emit()}onSaveDraft(){const e=this.form.getRawValue(),t=this.config(),a=t.formId;a&&this.formService.saveDraft(a,e,this.allFields());const n=t.wizard;if(n?.autoSave&&n.saveEndpoint&&this.submitAdapter){const o=this.formService.buildPayload(t,e,this.allFields());this.submitAdapter.submit({endpoint:n.saveEndpoint,method:n.saveMethod??"POST",body:o}).subscribe({error:r=>this.formError.emit(r)})}this.draftSaved.emit(e),this.draftRestored.set(!1),this.draftJustSaved.set(!0),this.draftSavedTimer&&clearTimeout(this.draftSavedTimer),this.draftSavedTimer=setTimeout(()=>this.draftJustSaved.set(!1),2500),this.toast?.show({variant:"success",message:this.locale()==="en"?"Draft saved":"تم حفظ المسودة"})}dismissDraftBanner(){this.draftRestored.set(!1)}onDiscardDraft(){this.draftRestored.set(!1),this.onClear()}onSubmit(){if(this.wizardEnabled()&&!this.isLastStep()){this.nextStep();return}if(this.markVisibleTouched(),!this.visibleValid()){this.toast?.show({variant:"error",message:this.locale()==="en"?"Please fix the highlighted fields":"يرجى تصحيح الحقول المحددة"});return}const e=this.config(),t=this.form.getRawValue(),a=new Set(this.collectVisibleFieldNames()),n={};for(const o of this.allFields())!a.has(o.name)&&o.type!=="hidden"||(n[o.name]=t[o.name]);if(e.emitOnly||!e.endpoint){this.formSubmitted.emit(n),this.toast?.show({variant:"success",message:this.labelOf(e.successMessage)||(this.locale()==="en"?"Submitted":"تم الإرسال")});return}this.submitting.set(!0),this.formService.submitForm(e,n,this.allFields()).subscribe({next:o=>{this.submitting.set(!1),this.formSubmitted.emit(o),e.formId&&this.formService.clearDraft(e.formId),this.toast?.show({variant:"success",message:this.labelOf(e.successMessage)||(this.locale()==="en"?"Submitted successfully":"تم الإرسال بنجاح")})},error:o=>{this.submitting.set(!1),this.formError.emit(o),this.toast?.show({variant:"error",message:this.labelOf(e.errorMessage)||(this.locale()==="en"?"Submission failed":"فشل الإرسال")})}})}patchFormValues(e){this.form.patchValue(e)}getForm(){return this.form}markStepTouched(){for(const e of this.visibleFields())this.form.get(e.name)?.markAsTouched()}markVisibleTouched(){for(const e of this.collectVisibleFieldNames())this.form.get(e)?.markAsTouched()}stepValid(){return this.config().wizard?.steps[this.activeStep()]?.optional?!0:this.visibleFields().every(t=>{const a=this.form.get(t.name);return!a||a.disabled||a.valid})}visibleValid(){return this.collectVisibleFieldNames().every(e=>{const t=this.form.get(e);return!t||t.disabled||t.valid})}collectVisibleFieldNames(){const e=this.formValues(),t=this.optionsMap(),a=this.wizardEnabled()?this.allFields():this.config().fields,n=o=>a.find(r=>r.name===o);return a.filter(o=>o.type==="hidden"||Y(o,e,r=>t[r],n)).map(o=>o.name)}static ɵfac=l.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:F,deps:[],target:l.ɵɵFactoryTarget.Component});static ɵcmp=l.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"22.1.5",type:F,isStandalone:!0,selector:"dga-dynamic-form",inputs:{config:{classPropertyName:"config",publicName:"config",isSignal:!0,isRequired:!0,transformFunction:null},localeInput:{classPropertyName:"localeInput",publicName:"locale",isSignal:!0,isRequired:!1,transformFunction:null}},outputs:{formSubmitted:"formSubmitted",formError:"formError",clearButtonClick:"clearButtonClick",draftSaved:"draftSaved",fileDownload:"fileDownload"},host:{classAttribute:"block w-full"},ngImport:l,template:`
654
+ <form
655
+ class="flex w-full flex-col"
656
+ [class.gap-2xl]="wizardEnabled()"
657
+ [class.gap-3xl]="!wizardEnabled()"
658
+ [formGroup]="form"
659
+ (ngSubmit)="onSubmit()"
660
+ >
585
661
  @if (descriptionText()) {
586
662
  <p class="text-body-md text-paragraph">{{ descriptionText() }}</p>
587
663
  }
588
664
 
589
665
  @if (wizardEnabled()) {
590
- <dga-stepper
591
- [steps]="stepperSteps()"
592
- [(active)]="activeStep"
593
- [beforeActiveChange]="canActivateStep"
594
- [label]="locale() === 'en' ? 'Steps' : 'الخطوات'"
595
- />
666
+ @if (draftRestored()) {
667
+ <dga-alert
668
+ variant="info"
669
+ display="inline"
670
+ color
671
+ [title]="draftRestoredTitle()"
672
+ [closeLabel]="locale() === 'en' ? 'Dismiss' : 'إغلاق'"
673
+ dismissible
674
+ (dismissed)="dismissDraftBanner()"
675
+ >
676
+ {{ draftRestoredBody() }}
677
+ <button
678
+ dgaAlertActions
679
+ dgaButton
680
+ type="button"
681
+ variant="ghost"
682
+ size="sm"
683
+ (click)="onDiscardDraft()"
684
+ >
685
+ {{ discardDraftLabel() }}
686
+ </button>
687
+ </dga-alert>
688
+ }
689
+
690
+ <dga-card appearance="outline" padding="lg">
691
+ <div class="flex flex-col gap-2xl">
692
+ <div
693
+ class="flex items-center gap-xl rounded-lg bg-sunken px-xl py-lg"
694
+ >
695
+ <dga-stepper
696
+ class="min-w-0 flex-1"
697
+ [steps]="stepperSteps()"
698
+ [(active)]="activeStep"
699
+ [beforeActiveChange]="canActivateStep"
700
+ [label]="stepProgressLabel()"
701
+ />
702
+ <div
703
+ class="flex shrink-0 items-center justify-center rounded-full bg-card p-sm shadow-xs"
704
+ >
705
+ <dga-progress
706
+ appearance="radial"
707
+ size="md"
708
+ [value]="stepPercent()"
709
+ [valueText]="stepFraction()"
710
+ [label]="stepProgressLabel()"
711
+ />
712
+ </div>
713
+ </div>
714
+
715
+ <div class="flex flex-col gap-xl">
716
+ <div class="flex flex-wrap items-start justify-between gap-md">
717
+ <div class="min-w-0 flex-1">
718
+ @if (currentStepTitle()) {
719
+ <h3 class="m-0 text-display-xs font-semibold text-display">
720
+ {{ currentStepTitle() }}
721
+ </h3>
722
+ }
723
+ @if (currentStepDescription()) {
724
+ <p class="m-0 mt-sm text-body-sm text-muted">
725
+ {{ currentStepDescription() }}
726
+ </p>
727
+ }
728
+ </div>
729
+ @if (currentStepOptional()) {
730
+ <dga-badge variant="neutral" size="sm">
731
+ {{ optionalBadgeLabel() }}
732
+ </dga-badge>
733
+ }
734
+ </div>
735
+ <ng-container *ngTemplateOutlet="fieldsTpl" />
736
+ </div>
737
+ </div>
738
+ <div
739
+ dgaCardActions
740
+ class="flex w-full flex-col gap-lg rounded-md bg-sunken px-xl py-lg sm:flex-row sm:items-center"
741
+ >
742
+ <ng-container *ngTemplateOutlet="actionsTpl" />
743
+ </div>
744
+ </dga-card>
745
+ } @else {
746
+ <ng-container *ngTemplateOutlet="fieldsTpl" />
747
+ <div class="flex w-full flex-col gap-lg sm:flex-row sm:items-center">
748
+ <ng-container *ngTemplateOutlet="actionsTpl" />
749
+ </div>
596
750
  }
751
+ </form>
597
752
 
753
+ <ng-template #fieldsTpl>
598
754
  <div class="grid grid-cols-12 gap-xl">
599
755
  @for (field of visibleFields(); track field.name) {
600
756
  <div [class]="columnClass(field)">
@@ -616,29 +772,22 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
616
772
  </div>
617
773
  }
618
774
  </div>
775
+ </ng-template>
619
776
 
777
+ <ng-template #actionsTpl>
620
778
  <div class="flex flex-wrap items-center gap-md">
621
- @if (wizardEnabled() && activeStep() > 0) {
779
+ @if (wizardEnabled()) {
622
780
  <button
623
781
  dgaButton
624
782
  type="button"
625
- variant="secondary"
783
+ variant="outlineMuted"
784
+ [disabled]="activeStep() === 0"
626
785
  (click)="previousStep()"
627
786
  >
787
+ <dga-icon name="arrowPrev" size="sm" />
628
788
  {{ previousLabel() }}
629
789
  </button>
630
790
  }
631
-
632
- @if (wizardEnabled() && !isLastStep()) {
633
- <button dgaButton type="button" (click)="nextStep()">
634
- {{ nextLabel() }}
635
- </button>
636
- } @else {
637
- <button dgaButton type="submit" [disabled]="submitting()">
638
- {{ submitLabel() }}
639
- </button>
640
- }
641
-
642
791
  @if (config().clearButtonLabel) {
643
792
  <button
644
793
  dgaButton
@@ -649,34 +798,133 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
649
798
  {{ clearLabel() }}
650
799
  </button>
651
800
  }
801
+ </div>
652
802
 
803
+ <div class="flex flex-wrap items-center gap-md sm:ms-auto">
653
804
  @if (showSave()) {
654
805
  <button
655
806
  dgaButton
656
807
  type="button"
657
- variant="secondary"
808
+ variant="ghost"
658
809
  (click)="onSaveDraft()"
659
810
  >
660
- {{ saveLabel() }}
811
+ <dga-icon [name]="draftJustSaved() ? 'check' : 'folder'" size="sm" />
812
+ {{ draftJustSaved() ? draftSavedLabel() : saveLabel() }}
813
+ </button>
814
+ }
815
+ @if (wizardEnabled() && !isLastStep()) {
816
+ <button dgaButton type="button" (click)="nextStep()">
817
+ {{ nextLabel() }}
818
+ <dga-icon name="arrowNext" size="sm" />
819
+ </button>
820
+ } @else {
821
+ <button dgaButton type="submit" [disabled]="submitting()" [loading]="submitting()">
822
+ {{ submitLabel() }}
661
823
  </button>
662
824
  }
663
825
  </div>
664
- </form>
665
- `,isInline:!0,dependencies:[{kind:"ngmodule",type:O},{kind:"directive",type:h.ɵNgNoValidate,selector:"form:not([ngNoForm]):not([ngNativeValidate])"},{kind:"directive",type:h.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:h.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"component",type:D,selector:"dga-dynamic-form-field",inputs:["field","form","locale","required","error","runtimeOptions","consentBaseUrl"],outputs:["optionsLoaded","fileDownload"]},{kind:"directive",type:A,selector:"button[dgaButton], a[dgaButton]",inputs:["variant","size","iconOnly","cooldownMs","loading","dgaButtonClass"]},{kind:"component",type:me,selector:"dga-stepper",inputs:["steps","active","label","beforeActiveChange"],outputs:["activeChange"]},{kind:"directive",type:X,selector:"[ngComponentOutlet]",inputs:["ngComponentOutlet","ngComponentOutletInputs","ngComponentOutletInjector","ngComponentOutletEnvironmentInjector","ngComponentOutletContent","ngComponentOutletNgModule"],exportAs:["ngComponentOutlet"]}],changeDetection:l.ChangeDetectionStrategy.OnPush})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.0.7",ngImport:l,type:N,decorators:[{type:R,args:[{selector:"dga-dynamic-form",standalone:!0,imports:[O,D,A,me,X],template:`
666
- <form class="flex w-full flex-col gap-3xl" [formGroup]="form" (ngSubmit)="onSubmit()">
826
+ </ng-template>
827
+ `,isInline:!0,dependencies:[{kind:"ngmodule",type:O},{kind:"directive",type:b.ɵNgNoValidate,selector:"form:not([ngNoForm]):not([ngNativeValidate])"},{kind:"directive",type:b.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:b.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"component",type:w,selector:"dga-dynamic-form-field",inputs:["field","form","locale","required","error","runtimeOptions","consentBaseUrl"],outputs:["optionsLoaded","fileDownload"]},{kind:"component",type:be,selector:"dga-alert",inputs:["variant","title","shadow","color","display","dismissible","closeLabel"],outputs:["dismissed"]},{kind:"component",type:ve,selector:"dga-badge",inputs:["variant","size"]},{kind:"directive",type:E,selector:"button[dgaButton], a[dgaButton]",inputs:["variant","size","iconOnly","cooldownMs","loading","dgaButtonClass"]},{kind:"component",type:ye,selector:"dga-card",inputs:["appearance","padding","border","shadow","title","description","titleTruncate","descriptionClamp","icon","iconSize","iconAppearance","badges","badgeSize","primaryLabel","primaryHref","primaryVariant","secondaryLabel","secondaryHref","secondaryVariant","actionSize","actionsAlign","interactive"],outputs:["primaryClick","secondaryClick"]},{kind:"component",type:C,selector:"dga-icon",inputs:["name","glyph","size","tone","label"]},{kind:"component",type:xe,selector:"dga-progress",inputs:["value","size","tone","appearance","showValue","valueText","label"]},{kind:"component",type:De,selector:"dga-stepper",inputs:["steps","active","label","beforeActiveChange"],outputs:["activeChange"]},{kind:"directive",type:te,selector:"[ngComponentOutlet]",inputs:["ngComponentOutlet","ngComponentOutletInputs","ngComponentOutletInjector","ngComponentOutletEnvironmentInjector","ngComponentOutletContent","ngComponentOutletNgModule"],exportAs:["ngComponentOutlet"]},{kind:"directive",type:ae,selector:"[ngTemplateOutlet]",inputs:["ngTemplateOutletContext","ngTemplateOutlet","ngTemplateOutletInjector"]}],changeDetection:l.ChangeDetectionStrategy.OnPush})}l.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"22.1.5",ngImport:l,type:F,decorators:[{type:V,args:[{selector:"dga-dynamic-form",standalone:!0,imports:[O,w,be,ve,E,ye,C,xe,De,te,ae],template:`
828
+ <form
829
+ class="flex w-full flex-col"
830
+ [class.gap-2xl]="wizardEnabled()"
831
+ [class.gap-3xl]="!wizardEnabled()"
832
+ [formGroup]="form"
833
+ (ngSubmit)="onSubmit()"
834
+ >
667
835
  @if (descriptionText()) {
668
836
  <p class="text-body-md text-paragraph">{{ descriptionText() }}</p>
669
837
  }
670
838
 
671
839
  @if (wizardEnabled()) {
672
- <dga-stepper
673
- [steps]="stepperSteps()"
674
- [(active)]="activeStep"
675
- [beforeActiveChange]="canActivateStep"
676
- [label]="locale() === 'en' ? 'Steps' : 'الخطوات'"
677
- />
840
+ @if (draftRestored()) {
841
+ <dga-alert
842
+ variant="info"
843
+ display="inline"
844
+ color
845
+ [title]="draftRestoredTitle()"
846
+ [closeLabel]="locale() === 'en' ? 'Dismiss' : 'إغلاق'"
847
+ dismissible
848
+ (dismissed)="dismissDraftBanner()"
849
+ >
850
+ {{ draftRestoredBody() }}
851
+ <button
852
+ dgaAlertActions
853
+ dgaButton
854
+ type="button"
855
+ variant="ghost"
856
+ size="sm"
857
+ (click)="onDiscardDraft()"
858
+ >
859
+ {{ discardDraftLabel() }}
860
+ </button>
861
+ </dga-alert>
862
+ }
863
+
864
+ <dga-card appearance="outline" padding="lg">
865
+ <div class="flex flex-col gap-2xl">
866
+ <div
867
+ class="flex items-center gap-xl rounded-lg bg-sunken px-xl py-lg"
868
+ >
869
+ <dga-stepper
870
+ class="min-w-0 flex-1"
871
+ [steps]="stepperSteps()"
872
+ [(active)]="activeStep"
873
+ [beforeActiveChange]="canActivateStep"
874
+ [label]="stepProgressLabel()"
875
+ />
876
+ <div
877
+ class="flex shrink-0 items-center justify-center rounded-full bg-card p-sm shadow-xs"
878
+ >
879
+ <dga-progress
880
+ appearance="radial"
881
+ size="md"
882
+ [value]="stepPercent()"
883
+ [valueText]="stepFraction()"
884
+ [label]="stepProgressLabel()"
885
+ />
886
+ </div>
887
+ </div>
888
+
889
+ <div class="flex flex-col gap-xl">
890
+ <div class="flex flex-wrap items-start justify-between gap-md">
891
+ <div class="min-w-0 flex-1">
892
+ @if (currentStepTitle()) {
893
+ <h3 class="m-0 text-display-xs font-semibold text-display">
894
+ {{ currentStepTitle() }}
895
+ </h3>
896
+ }
897
+ @if (currentStepDescription()) {
898
+ <p class="m-0 mt-sm text-body-sm text-muted">
899
+ {{ currentStepDescription() }}
900
+ </p>
901
+ }
902
+ </div>
903
+ @if (currentStepOptional()) {
904
+ <dga-badge variant="neutral" size="sm">
905
+ {{ optionalBadgeLabel() }}
906
+ </dga-badge>
907
+ }
908
+ </div>
909
+ <ng-container *ngTemplateOutlet="fieldsTpl" />
910
+ </div>
911
+ </div>
912
+ <div
913
+ dgaCardActions
914
+ class="flex w-full flex-col gap-lg rounded-md bg-sunken px-xl py-lg sm:flex-row sm:items-center"
915
+ >
916
+ <ng-container *ngTemplateOutlet="actionsTpl" />
917
+ </div>
918
+ </dga-card>
919
+ } @else {
920
+ <ng-container *ngTemplateOutlet="fieldsTpl" />
921
+ <div class="flex w-full flex-col gap-lg sm:flex-row sm:items-center">
922
+ <ng-container *ngTemplateOutlet="actionsTpl" />
923
+ </div>
678
924
  }
925
+ </form>
679
926
 
927
+ <ng-template #fieldsTpl>
680
928
  <div class="grid grid-cols-12 gap-xl">
681
929
  @for (field of visibleFields(); track field.name) {
682
930
  <div [class]="columnClass(field)">
@@ -698,29 +946,22 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
698
946
  </div>
699
947
  }
700
948
  </div>
949
+ </ng-template>
701
950
 
951
+ <ng-template #actionsTpl>
702
952
  <div class="flex flex-wrap items-center gap-md">
703
- @if (wizardEnabled() && activeStep() > 0) {
953
+ @if (wizardEnabled()) {
704
954
  <button
705
955
  dgaButton
706
956
  type="button"
707
- variant="secondary"
957
+ variant="outlineMuted"
958
+ [disabled]="activeStep() === 0"
708
959
  (click)="previousStep()"
709
960
  >
961
+ <dga-icon name="arrowPrev" size="sm" />
710
962
  {{ previousLabel() }}
711
963
  </button>
712
964
  }
713
-
714
- @if (wizardEnabled() && !isLastStep()) {
715
- <button dgaButton type="button" (click)="nextStep()">
716
- {{ nextLabel() }}
717
- </button>
718
- } @else {
719
- <button dgaButton type="submit" [disabled]="submitting()">
720
- {{ submitLabel() }}
721
- </button>
722
- }
723
-
724
965
  @if (config().clearButtonLabel) {
725
966
  <button
726
967
  dgaButton
@@ -731,17 +972,30 @@ import*as l from"@angular/core";import{InjectionToken as y,inject as c,Injectabl
731
972
  {{ clearLabel() }}
732
973
  </button>
733
974
  }
975
+ </div>
734
976
 
977
+ <div class="flex flex-wrap items-center gap-md sm:ms-auto">
735
978
  @if (showSave()) {
736
979
  <button
737
980
  dgaButton
738
981
  type="button"
739
- variant="secondary"
982
+ variant="ghost"
740
983
  (click)="onSaveDraft()"
741
984
  >
742
- {{ saveLabel() }}
985
+ <dga-icon [name]="draftJustSaved() ? 'check' : 'folder'" size="sm" />
986
+ {{ draftJustSaved() ? draftSavedLabel() : saveLabel() }}
987
+ </button>
988
+ }
989
+ @if (wizardEnabled() && !isLastStep()) {
990
+ <button dgaButton type="button" (click)="nextStep()">
991
+ {{ nextLabel() }}
992
+ <dga-icon name="arrowNext" size="sm" />
993
+ </button>
994
+ } @else {
995
+ <button dgaButton type="submit" [disabled]="submitting()" [loading]="submitting()">
996
+ {{ submitLabel() }}
743
997
  </button>
744
998
  }
745
999
  </div>
746
- </form>
747
- `,changeDetection:V.OnPush,host:{class:"block w-full"}}]}],ctorParameters:()=>[],propDecorators:{config:[{type:l.Input,args:[{isSignal:!0,alias:"config",required:!0}]}],localeInput:[{type:l.Input,args:[{isSignal:!0,alias:"locale",required:!1}]}],formSubmitted:[{type:l.Output,args:["formSubmitted"]}],formError:[{type:l.Output,args:["formError"]}],clearButtonClick:[{type:l.Output,args:["clearButtonClick"]}],draftSaved:[{type:l.Output,args:["draftSaved"]}],fileDownload:[{type:l.Output,args:["fileDownload"]}]}});export{_ as DGA_CAPTCHA_ADAPTER,ve as DGA_DATE_RANGE_PAIRS,be as DGA_DYNAMIC_FORM_FIELD_REGISTRY,U as DGA_FORM_I18N_ADAPTER,B as DGA_FORM_TOAST_ADAPTER,$ as DGA_HTTP_SECURITY,z as DGA_LOOKUP_ADAPTER,E as DGA_SUBMIT_ADAPTER,N as DgaDynamicForm,D as DgaDynamicFormField,L as DgaDynamicFormFieldRegistry,C as DgaDynamicFormService,Re as createDgaToastAdapter,Pe as createHttpLookupAdapter,Ve as createHttpSubmitAdapter,Ae as defaultFormI18nAdapter,De as dgaApplyCrossFieldValidators,F as dgaAssertSafeHttpUrl,Ce as dgaBuildFormGroup,G as dgaBuildValidators,Le as dgaCollectFields,Ie as dgaCreateControl,Se as dgaDefaultValueForField,Te as dgaDetectLocale,ke as dgaFindSelectedOption,W as dgaIsFieldVisible,K as dgaIsRequiredWhen,ye as dgaPasswordMatchValidator,I as dgaResolveLabel,xe as dgaSaudiMobileValidator,Fe as dgaSetControlValidators,Me as emptyLookupAdapter,Ee as noopCaptchaAdapter,dt as provideDgaDynamicForm,ut as resolveFormLabel};
1000
+ </ng-template>
1001
+ `,changeDetection:R.OnPush,host:{class:"block w-full"}}]}],ctorParameters:()=>[],propDecorators:{config:[{type:l.Input,args:[{isSignal:!0,alias:"config",required:!0}]}],localeInput:[{type:l.Input,args:[{isSignal:!0,alias:"locale",required:!1}]}],formSubmitted:[{type:l.Output,args:["formSubmitted"]}],formError:[{type:l.Output,args:["formError"]}],clearButtonClick:[{type:l.Output,args:["clearButtonClick"]}],draftSaved:[{type:l.Output,args:["draftSaved"]}],fileDownload:[{type:l.Output,args:["fileDownload"]}]}});export{_ as DGA_CAPTCHA_ADAPTER,Le as DGA_DATE_RANGE_PAIRS,Ce as DGA_DYNAMIC_FORM_FIELD_REGISTRY,G as DGA_FORM_I18N_ADAPTER,$ as DGA_FORM_TOAST_ADAPTER,U as DGA_HTTP_SECURITY,B as DGA_LOOKUP_ADAPTER,M as DGA_SUBMIT_ADAPTER,F as DgaDynamicForm,w as DgaDynamicFormField,k as DgaDynamicFormFieldRegistry,I as DgaDynamicFormService,Ue as createDgaToastAdapter,$e as createHttpLookupAdapter,_e as createHttpSubmitAdapter,Ve as defaultFormI18nAdapter,Te as dgaApplyCrossFieldValidators,T as dgaAssertSafeHttpUrl,Ae as dgaBuildFormGroup,W as dgaBuildValidators,Ee as dgaCollectFields,Oe as dgaCreateControl,Ne as dgaDefaultValueForField,Re as dgaDetectLocale,Pe as dgaFindSelectedOption,Y as dgaIsFieldVisible,J as dgaIsRequiredWhen,Ie as dgaPasswordMatchValidator,ke as dgaPhoneFieldValidator,m as dgaResolveLabel,j as dgaSaudiMobileValidator,K as dgaSetControlValidators,Be as emptyLookupAdapter,qe as noopCaptchaAdapter,ht as provideDgaDynamicForm,bt as resolveFormLabel};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a.nemreen/dga-dynamic-form",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "Schema-driven Angular dynamic forms — 18 field types, wizard, cross-field validators (password, Saudi phone, date range), staged uploads, lookup, reCAPTCHA v3, and conditional visibility. Built on @a.nemreen/dga-ui.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1,14 +1,16 @@
1
+ import * as _a_nemreen_dga_dynamic_form from '@a.nemreen/dga-dynamic-form';
1
2
  import * as _angular_core from '@angular/core';
2
3
  import { InjectionToken, Type, OnInit, EnvironmentProviders } from '@angular/core';
3
4
  import { FormGroup, FormControl, FormBuilder, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
4
- import { DgaStep, DgaSelectOption, DgaToastService } from '@a.nemreen/dga-ui';
5
+ import { DgaStep, DgaPhoneCountry, DgaSelectOption, DgaToastService } from '@a.nemreen/dga-ui';
5
6
  import { Observable } from 'rxjs';
6
7
  import { HttpClient } from '@angular/common/http';
7
8
 
8
- /** Bilingual label used across the schema. */
9
+ /** Localized label (en/ar required for schemas; extra locale keys optional). */
9
10
  interface DgaFormLabel {
10
11
  en: string;
11
12
  ar: string;
13
+ [locale: string]: string;
12
14
  }
13
15
  interface DgaFormFieldColumns {
14
16
  xl?: number;
@@ -43,6 +45,15 @@ interface DgaStagedFileRef {
43
45
  listKey?: string;
44
46
  }
45
47
  type DgaInputRestriction = 'numbers' | 'english' | 'arabic';
48
+ /** Extra dial code for `type: 'phone'` (KSA `+966` is always first). */
49
+ interface DgaFormPhoneCountry {
50
+ /** Dial code including `+`, e.g. `+971`. */
51
+ code: string;
52
+ /** Short prefix-select label. Defaults to `code`. */
53
+ label?: string;
54
+ /** Searchable country name. */
55
+ name?: DgaFormLabel;
56
+ }
46
57
  type DgaHttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
47
58
  interface DgaFormField {
48
59
  name: string;
@@ -100,11 +111,20 @@ interface DgaFormField {
100
111
  monthPicker?: boolean;
101
112
  maxChips?: number;
102
113
  allowDuplicates?: boolean;
114
+ /**
115
+ * Extra dial codes for `type: 'phone'`.
116
+ * KSA (`+966`) is always first; duplicate codes are skipped.
117
+ */
118
+ countries?: DgaFormPhoneCountry[];
103
119
  disabled?: boolean;
104
120
  readonly?: boolean;
105
121
  value?: unknown;
106
122
  columns?: DgaFormFieldColumns;
107
- optionsLayout?: 'stack' | 'grid';
123
+ /**
124
+ * Radio / checkbox arrangement.
125
+ * `cards` renders each option as a selectable DGA card (title + description).
126
+ */
127
+ optionsLayout?: 'stack' | 'grid' | 'cards';
108
128
  excludeFromPayload?: boolean;
109
129
  /**
110
130
  * When true, the field value is never written to draft storage.
@@ -182,7 +202,7 @@ interface DgaDynamicFormConfig {
182
202
  */
183
203
  consentBaseUrl?: string;
184
204
  }
185
- type DgaFormLocale = 'ar' | 'en';
205
+ type DgaFormLocale = string;
186
206
 
187
207
  interface DgaLookupRequest {
188
208
  domain: string;
@@ -216,11 +236,8 @@ interface DgaCaptchaAdapter {
216
236
  getToken(action?: string): Observable<string | null>;
217
237
  }
218
238
  interface DgaFormI18nAdapter {
219
- /** Resolve a bilingual label for the active locale. */
220
- resolveLabel(label: {
221
- en: string;
222
- ar: string;
223
- }, locale: DgaFormLocale): string;
239
+ /** Resolve a localized label for the active locale. */
240
+ resolveLabel(label: DgaFormLabel, locale: DgaFormLocale): string;
224
241
  /** Optional free-form key lookup (e.g. ngx-translate). */
225
242
  translate?(key: string, locale: DgaFormLocale): string;
226
243
  }
@@ -268,7 +285,7 @@ declare class DgaDynamicFormFieldRegistry {
268
285
 
269
286
  declare class DgaDynamicForm {
270
287
  readonly config: _angular_core.InputSignal<DgaDynamicFormConfig>;
271
- readonly localeInput: _angular_core.InputSignal<DgaFormLocale | null>;
288
+ readonly localeInput: _angular_core.InputSignal<string | null>;
272
289
  readonly formSubmitted: _angular_core.OutputEmitterRef<unknown>;
273
290
  readonly formError: _angular_core.OutputEmitterRef<unknown>;
274
291
  readonly clearButtonClick: _angular_core.OutputEmitterRef<void>;
@@ -286,25 +303,38 @@ declare class DgaDynamicForm {
286
303
  private valueChangesSub;
287
304
  readonly activeStep: _angular_core.WritableSignal<number>;
288
305
  readonly submitting: _angular_core.WritableSignal<boolean>;
306
+ readonly draftRestored: _angular_core.WritableSignal<boolean>;
307
+ readonly draftJustSaved: _angular_core.WritableSignal<boolean>;
289
308
  readonly optionsMap: _angular_core.WritableSignal<Record<string, DgaFormFieldOption[]>>;
290
309
  private readonly formValues;
291
- readonly locale: _angular_core.Signal<DgaFormLocale>;
310
+ private draftSavedTimer;
311
+ readonly locale: _angular_core.Signal<string>;
292
312
  readonly wizardEnabled: _angular_core.Signal<boolean>;
293
313
  readonly allFields: _angular_core.Signal<DgaFormField[]>;
294
314
  readonly currentStepFields: _angular_core.Signal<DgaFormField[]>;
295
315
  readonly visibleFields: _angular_core.Signal<DgaFormField[]>;
296
316
  readonly stepperSteps: _angular_core.Signal<DgaStep[]>;
297
317
  readonly descriptionText: _angular_core.Signal<string>;
318
+ readonly currentStep: _angular_core.Signal<_a_nemreen_dga_dynamic_form.DgaFormStep | undefined>;
319
+ readonly currentStepTitle: _angular_core.Signal<string>;
320
+ readonly currentStepDescription: _angular_core.Signal<string>;
321
+ readonly currentStepOptional: _angular_core.Signal<boolean>;
322
+ readonly stepCount: _angular_core.Signal<number>;
323
+ readonly stepPercent: _angular_core.Signal<number>;
324
+ stepFraction(): string;
298
325
  constructor();
299
- labelOf(label: {
300
- en: string;
301
- ar: string;
302
- } | undefined): string;
326
+ labelOf(label: DgaFormLabel | undefined): string;
303
327
  submitLabel(): string;
304
328
  nextLabel(): string;
305
329
  previousLabel(): string;
306
330
  clearLabel(): string;
307
331
  saveLabel(): string;
332
+ draftSavedLabel(): string;
333
+ draftRestoredTitle(): string;
334
+ draftRestoredBody(): string;
335
+ discardDraftLabel(): string;
336
+ optionalBadgeLabel(): string;
337
+ stepProgressLabel(): string;
308
338
  showSave(): boolean;
309
339
  isLastStep(): boolean;
310
340
  isRequired(field: DgaFormField): boolean;
@@ -323,6 +353,8 @@ declare class DgaDynamicForm {
323
353
  private skipEmptyStepsForward;
324
354
  onClear(): void;
325
355
  onSaveDraft(): void;
356
+ dismissDraftBanner(): void;
357
+ onDiscardDraft(): void;
326
358
  onSubmit(): void;
327
359
  /** Imperative helpers for host apps. */
328
360
  patchFormValues(values: Record<string, unknown>): void;
@@ -339,7 +371,7 @@ declare class DgaDynamicForm {
339
371
  declare class DgaDynamicFormField implements OnInit {
340
372
  readonly field: _angular_core.InputSignal<DgaFormField>;
341
373
  readonly form: _angular_core.InputSignal<FormGroup<any>>;
342
- readonly locale: _angular_core.InputSignal<DgaFormLocale>;
374
+ readonly locale: _angular_core.InputSignal<string>;
343
375
  readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
344
376
  readonly error: _angular_core.InputSignal<string>;
345
377
  readonly runtimeOptions: _angular_core.InputSignal<DgaFormFieldOption[]>;
@@ -358,13 +390,23 @@ declare class DgaDynamicFormField implements OnInit {
358
390
  readonly labelText: _angular_core.Signal<string>;
359
391
  readonly hintText: _angular_core.Signal<string>;
360
392
  readonly placeholderText: _angular_core.Signal<string>;
393
+ /** Extra dial codes after KSA; empty/invalid entries are dropped. */
394
+ readonly phoneCountries: _angular_core.Signal<DgaPhoneCountry[]>;
361
395
  readonly resolvedOptions: _angular_core.Signal<DgaFormFieldOption[]>;
396
+ readonly optionGroups: _angular_core.Signal<{
397
+ label: string;
398
+ options: DgaFormFieldOption[];
399
+ }[]>;
362
400
  readonly selectOptions: _angular_core.Signal<DgaSelectOption[]>;
363
401
  ngOnInit(): void;
364
402
  private mapExistingFileValue;
365
403
  private fetchConsent;
366
404
  private fetchLookup;
367
405
  optionLabel(opt: DgaFormFieldOption): string;
406
+ optionDescription(opt: DgaFormFieldOption): string;
407
+ isCardsLayout(): boolean;
408
+ optionsContainerClass(): string;
409
+ choiceLabelClass(opt: DgaFormFieldOption): string;
368
410
  isPasswordField(): boolean;
369
411
  isChecked(value: unknown): boolean;
370
412
  toggleCheckbox(value: unknown, checked: boolean): void;
@@ -418,6 +460,13 @@ declare function dgaIsRequiredWhen(field: DgaFormField, values: Record<string, u
418
460
  /** Known start/end date field pairs used by cross-field validators. */
419
461
  declare const DGA_DATE_RANGE_PAIRS: ReadonlyArray<[string, string]>;
420
462
  declare function dgaPasswordMatchValidator(passwordControl: AbstractControl): ValidatorFn;
463
+ /**
464
+ * Phone validator used by `type: phone`.
465
+ * KSA numbers keep Saudi mobile rules; extra `countries` codes only require digits.
466
+ */
467
+ declare function dgaPhoneFieldValidator(extraCountries?: ReadonlyArray<{
468
+ code?: string;
469
+ } | null | undefined>): ValidatorFn;
421
470
  /**
422
471
  * Saudi mobile validation for type: phone fields.
423
472
  * Accepts E.164 (+966…) or national digits (0xxxxxxxx or 5xxxxxxxx).
@@ -426,7 +475,7 @@ declare function dgaSaudiMobileValidator(control: AbstractControl): ValidationEr
426
475
  /** Wire password match + date-range validators after form group is built. */
427
476
  declare function dgaApplyCrossFieldValidators(form: FormGroup, fields: DgaFormField[]): void;
428
477
 
429
- /** Default bilingual label resolver. */
478
+ /** Default localized label resolver. */
430
479
  declare const defaultFormI18nAdapter: DgaFormI18nAdapter;
431
480
  /** No-op captcha (always null token). */
432
481
  declare const noopCaptchaAdapter: DgaCaptchaAdapter;
@@ -467,13 +516,11 @@ interface ProvideDgaDynamicFormOptions {
467
516
  */
468
517
  declare function provideDgaDynamicForm(options?: ProvideDgaDynamicFormOptions): EnvironmentProviders;
469
518
  /** Helper for templates / host apps. */
470
- declare function resolveFormLabel(label: {
471
- en: string;
472
- ar: string;
473
- } | undefined, locale: DgaFormLocale, fallback?: string): string;
519
+ declare function resolveFormLabel(label: DgaFormLabel | undefined, locale: DgaFormLocale, fallback?: string): string;
474
520
 
475
- declare function dgaResolveLabel(label: DgaFormLabel | undefined, locale: DgaFormLocale, fallback?: string): string;
521
+ type LocalizedMap = Partial<Record<string, string>>;
522
+ declare function dgaResolveLabel(label: LocalizedMap | DgaFormLabel | undefined, locale: DgaFormLocale, fallback?: string): string;
476
523
  declare function dgaDetectLocale(): DgaFormLocale;
477
524
 
478
- 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 };
479
- 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 };
525
+ 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, dgaPhoneFieldValidator, dgaResolveLabel, dgaSaudiMobileValidator, dgaSetControlValidators, emptyLookupAdapter, noopCaptchaAdapter, provideDgaDynamicForm, resolveFormLabel };
526
+ export type { DgaCaptchaAdapter, DgaDynamicFieldHostContext, DgaDynamicFieldRenderer, DgaDynamicFormConfig, DgaDynamicFormPayloadTransformer, DgaExistingAttachment, DgaFormField, DgaFormFieldColumns, DgaFormFieldOption, DgaFormFieldType, DgaFormI18nAdapter, DgaFormLabel, DgaFormLocale, DgaFormPhoneCountry, DgaFormStep, DgaFormToastAdapter, DgaHttpAdapterSecurityOptions, DgaHttpMethod, DgaHttpSecurityOptions, DgaInputRestriction, DgaLookupAdapter, DgaLookupRequest, DgaStagedFileRef, DgaSubmitAdapter, DgaSubmitRequest, DgaToastVariant, DgaWizardConfig, ProvideDgaDynamicFormOptions };