@yomotech/view-toolkit 1.0.30 → 1.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,10 @@
1
- import * as i1$1 from '@angular/common';
1
+ import * as i5 from '@angular/common';
2
2
  import { CommonModule, NgIf } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { EventEmitter, HostListener, Output, Input, Component, signal, Injectable, Directive, inject, Pipe, ElementRef, NgZone, ViewChild, forwardRef, HostBinding, NgModule, effect, DestroyRef } from '@angular/core';
4
+ import { EventEmitter, HostListener, Output, Input, Component, signal, Injectable, Directive, inject, Pipe, ElementRef, NgZone, ViewChild, HostBinding, NgModule, effect, DestroyRef } from '@angular/core';
5
5
  import * as i2 from '@angular/forms';
6
- import { FormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
7
- import * as i1$2 from '@angular/common/http';
6
+ import { FormsModule } from '@angular/forms';
7
+ import * as i1$1 from '@angular/common/http';
8
8
  import { HttpHeaders, HttpParams } from '@angular/common/http';
9
9
  import { Capacitor, CapacitorHttp } from '@capacitor/core';
10
10
  import * as i11 from 'ng-zorro-antd/icon';
@@ -13,7 +13,7 @@ import * as i13 from 'ng-zorro-antd/button';
13
13
  import { NzButtonModule } from 'ng-zorro-antd/button';
14
14
  import * as i4 from 'ng-zorro-antd/date-picker';
15
15
  import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
16
- import * as i5 from 'ng-zorro-antd/modal';
16
+ import * as i5$1 from 'ng-zorro-antd/modal';
17
17
  import { NzModalModule } from 'ng-zorro-antd/modal';
18
18
  import * as i12 from 'primeng/datepicker';
19
19
  import { DatePickerModule } from 'primeng/datepicker';
@@ -31,7 +31,7 @@ import * as i3$1 from 'primeng/inputtext';
31
31
  import { InputTextModule } from 'primeng/inputtext';
32
32
  import * as i18 from 'primeng/autocomplete';
33
33
  import { AutoCompleteModule } from 'primeng/autocomplete';
34
- import * as i1$3 from 'ngx-toastr';
34
+ import * as i1$2 from 'ngx-toastr';
35
35
  import * as i7 from 'angular-oauth2-oidc';
36
36
  import * as i14 from 'ng-zorro-antd/core/transition-patch';
37
37
  import * as i15 from 'ng-zorro-antd/core/wave';
@@ -47,14 +47,12 @@ import * as i11$1 from 'ng-zorro-antd/menu';
47
47
  import { NzMenuModule } from 'ng-zorro-antd/menu';
48
48
  import * as i4$1 from '@okta/okta-angular';
49
49
  import { OktaAuthStateService, OKTA_AUTH } from '@okta/okta-angular';
50
- import * as i4$2 from 'primeng/textarea';
51
- import { TextareaModule } from 'primeng/textarea';
52
- import * as i1$4 from 'primeng/dynamicdialog';
50
+ import * as i1$3 from 'primeng/dynamicdialog';
53
51
  import { DynamicDialogModule } from 'primeng/dynamicdialog';
54
52
  import { SelectModule } from 'primeng/select';
55
53
  import { Device } from '@capacitor/device';
56
54
  import { jwtDecode } from 'jwt-decode';
57
- import * as i1$5 from 'ng-zorro-antd/i18n';
55
+ import * as i1$4 from 'ng-zorro-antd/i18n';
58
56
  import { de_DE, en_US } from 'ng-zorro-antd/i18n';
59
57
  import * as i13$1 from 'ng-zorro-antd/tooltip';
60
58
  import { NzTooltipModule } from 'ng-zorro-antd/tooltip';
@@ -292,7 +290,7 @@ class PaginationComponent {
292
290
  this.onGoToPage.emit(page);
293
291
  }
294
292
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: PaginationComponent, deps: [{ token: i1.Platform }], target: i0.ɵɵFactoryTarget.Component });
295
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: PaginationComponent, isStandalone: true, selector: "vl-pagination", inputs: { itemInfo: "itemInfo", pageSize: "pageSize", currentPage: "currentPage", selectedLanguageInfo: "selectedLanguageInfo", pages: "pages" }, outputs: { pageSizeChange: "pageSizeChange", onGoToPage: "onGoToPage" }, host: { listeners: { "window:resize": "onWindowResize()" } }, ngImport: i0, template: "<div [ngStyle]=\"paginationStyle\">\r\n\r\n <ul [ngStyle]=\"listStyle\" [style.z-index]=\"'auto'\">\r\n\r\n <!-- Existing desktop pagination: unchanged -->\r\n <ng-container *ngIf=\"!isMobile\">\r\n\r\n <li (click)=\"invokeGoToPage(0)\" [ngStyle]=\"getPageItemStyle(undefined, currentPage === 0)\">\r\n \u00AB\u00AB\r\n </li>\r\n\r\n <li (click)=\"invokeGoToPage(currentPage - 1)\" [ngStyle]=\"getPageItemStyle(undefined, currentPage === 0)\">\r\n \u00AB\r\n </li>\r\n\r\n <li *ngFor=\"let page of pages\" (click)=\"invokeGoToPage(page)\" [ngStyle]=\"getPageItemStyle(page)\">\r\n {{ page + 1 }}\r\n </li>\r\n\r\n <li (click)=\"invokeGoToPage(currentPage + 1)\" [ngStyle]=\"\r\n getPageItemStyle(\r\n undefined,\r\n currentPage === pages.length - 1\r\n )\r\n \">\r\n \u00BB\r\n </li>\r\n\r\n <li (click)=\"invokeGoToPage(pages.length - 1)\" [ngStyle]=\"\r\n getPageItemStyle(\r\n undefined,\r\n currentPage === pages.length - 1\r\n )\r\n \">\r\n \u00BB\u00BB\r\n </li>\r\n\r\n </ng-container>\r\n\r\n <!-- Compact mobile pagination -->\r\n <ng-container *ngIf=\"isMobile\">\r\n\r\n <!-- First -->\r\n <li (click)=\"invokeGoToPage(0)\" [ngStyle]=\"getPageItemStyle(undefined, isFirstPage)\">\r\n \u00AB\u00AB\r\n </li>\r\n\r\n <!-- Previous -->\r\n <li (click)=\"invokeGoToPage(currentPage - 1)\" [ngStyle]=\"getPageItemStyle(undefined, isFirstPage)\">\r\n \u00AB\r\n </li>\r\n\r\n <!-- Current page / total pages -->\r\n <li [ngStyle]=\"pageSummaryStyle\">\r\n {{ currentPage + 1 }} / {{ pages.length }}\r\n </li>\r\n\r\n <!-- Next -->\r\n <li (click)=\"invokeGoToPage(currentPage + 1)\" [ngStyle]=\"getPageItemStyle(undefined, isLastPage)\">\r\n \u00BB\r\n </li>\r\n\r\n <!-- Last -->\r\n <li (click)=\"invokeGoToPage(pages.length - 1)\" [ngStyle]=\"getPageItemStyle(undefined, isLastPage)\">\r\n \u00BB\u00BB\r\n </li>\r\n\r\n </ng-container>\r\n\r\n </ul>\r\n\r\n <div *ngIf=\"itemInfo?.layoutSchema?.pagination?.pageSize\" [ngStyle]=\"sizeControlStyle\">\r\n <label for=\"pageSize\" [ngStyle]=\"labelStyle\">\r\n {{ getPaginationCaption() }}\r\n </label>\r\n\r\n <select id=\"pageSize\" [(ngModel)]=\"pageSize\" (change)=\"invokeOnPageSizeChange(pageSize)\"\r\n [ngStyle]=\"selectStyle\">\r\n <option *ngFor=\"\r\n let size of\r\n itemInfo?.layoutSchema\r\n ?.pagination?.pageSizeOptions ||\r\n [5, 10, 15, 20]\r\n \" [value]=\"size\">\r\n {{ size }}\r\n </option>\r\n </select>\r\n </div>\r\n\r\n</div>", styles: [":host{display:block;width:100%;min-width:0;box-sizing:border-box}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PlatformModule }] });
293
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: PaginationComponent, isStandalone: true, selector: "vl-pagination", inputs: { itemInfo: "itemInfo", pageSize: "pageSize", currentPage: "currentPage", selectedLanguageInfo: "selectedLanguageInfo", pages: "pages" }, outputs: { pageSizeChange: "pageSizeChange", onGoToPage: "onGoToPage" }, host: { listeners: { "window:resize": "onWindowResize()" } }, ngImport: i0, template: "<div [ngStyle]=\"paginationStyle\">\r\n\r\n <ul [ngStyle]=\"listStyle\" [style.z-index]=\"'auto'\">\r\n\r\n <!-- Existing desktop pagination: unchanged -->\r\n <ng-container *ngIf=\"!isMobile\">\r\n\r\n <li (click)=\"invokeGoToPage(0)\" [ngStyle]=\"getPageItemStyle(undefined, currentPage === 0)\">\r\n \u00AB\u00AB\r\n </li>\r\n\r\n <li (click)=\"invokeGoToPage(currentPage - 1)\" [ngStyle]=\"getPageItemStyle(undefined, currentPage === 0)\">\r\n \u00AB\r\n </li>\r\n\r\n <li *ngFor=\"let page of pages\" (click)=\"invokeGoToPage(page)\" [ngStyle]=\"getPageItemStyle(page)\">\r\n {{ page + 1 }}\r\n </li>\r\n\r\n <li (click)=\"invokeGoToPage(currentPage + 1)\" [ngStyle]=\"\r\n getPageItemStyle(\r\n undefined,\r\n currentPage === pages.length - 1\r\n )\r\n \">\r\n \u00BB\r\n </li>\r\n\r\n <li (click)=\"invokeGoToPage(pages.length - 1)\" [ngStyle]=\"\r\n getPageItemStyle(\r\n undefined,\r\n currentPage === pages.length - 1\r\n )\r\n \">\r\n \u00BB\u00BB\r\n </li>\r\n\r\n </ng-container>\r\n\r\n <!-- Compact mobile pagination -->\r\n <ng-container *ngIf=\"isMobile\">\r\n\r\n <!-- First -->\r\n <li (click)=\"invokeGoToPage(0)\" [ngStyle]=\"getPageItemStyle(undefined, isFirstPage)\">\r\n \u00AB\u00AB\r\n </li>\r\n\r\n <!-- Previous -->\r\n <li (click)=\"invokeGoToPage(currentPage - 1)\" [ngStyle]=\"getPageItemStyle(undefined, isFirstPage)\">\r\n \u00AB\r\n </li>\r\n\r\n <!-- Current page / total pages -->\r\n <li [ngStyle]=\"pageSummaryStyle\">\r\n {{ currentPage + 1 }} / {{ pages.length }}\r\n </li>\r\n\r\n <!-- Next -->\r\n <li (click)=\"invokeGoToPage(currentPage + 1)\" [ngStyle]=\"getPageItemStyle(undefined, isLastPage)\">\r\n \u00BB\r\n </li>\r\n\r\n <!-- Last -->\r\n <li (click)=\"invokeGoToPage(pages.length - 1)\" [ngStyle]=\"getPageItemStyle(undefined, isLastPage)\">\r\n \u00BB\u00BB\r\n </li>\r\n\r\n </ng-container>\r\n\r\n </ul>\r\n\r\n <div *ngIf=\"itemInfo?.layoutSchema?.pagination?.pageSize\" [ngStyle]=\"sizeControlStyle\">\r\n <label for=\"pageSize\" [ngStyle]=\"labelStyle\">\r\n {{ getPaginationCaption() }}\r\n </label>\r\n\r\n <select id=\"pageSize\" [(ngModel)]=\"pageSize\" (change)=\"invokeOnPageSizeChange(pageSize)\"\r\n [ngStyle]=\"selectStyle\">\r\n <option *ngFor=\"\r\n let size of\r\n itemInfo?.layoutSchema\r\n ?.pagination?.pageSizeOptions ||\r\n [5, 10, 15, 20]\r\n \" [value]=\"size\">\r\n {{ size }}\r\n </option>\r\n </select>\r\n </div>\r\n\r\n</div>", styles: [":host{display:block;width:100%;min-width:0;box-sizing:border-box}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PlatformModule }] });
296
294
  }
297
295
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: PaginationComponent, decorators: [{
298
296
  type: Component,
@@ -395,6 +393,57 @@ function resolveCustomKeyboardType(option, property) {
395
393
  }
396
394
  }
397
395
 
396
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-16
397
+ // - new: the toolkit keyboards (and the docked date picker) can be switched on in a browser. They
398
+ // belong to the app on iOS/Android; the Designer's preview shows the web build and, with "Mobile"
399
+ // or "Tablet" chosen, asks for them so the preview behaves like the app. Native-only calls
400
+ // (Capacitor Keyboard plugin) stay tied to the real device. A mouse there acts like a finger
401
+ // (treatMouseAsTouch), otherwise the first click lost the keyboard
402
+ let onWeb = false;
403
+ /** The app on an iOS/Android device. */
404
+ function nativeDevice() {
405
+ return Capacitor.isNativePlatform() && (Capacitor.getPlatform() === 'ios' || Capacitor.getPlatform() === 'android');
406
+ }
407
+ /** Turns the toolkit keyboards on or off in a browser (the Designer's preview, Mobile/Tablet). */
408
+ function setToolkitKeyboardOnWeb(on) {
409
+ onWeb = on;
410
+ if (on && !nativeDevice()) {
411
+ treatMouseAsTouch();
412
+ }
413
+ }
414
+ let mouseHooked = false;
415
+ /**
416
+ * In the browser a mouse click sends mousedown after the pointerdown, and its default action moves
417
+ * the focus: the toolkit keyboard of a field closed again, only a second click kept it. With the
418
+ * toolkit keyboards on in a browser, a mousedown on a field, a picker icon or the keyboard keeps the
419
+ * focus where the keyboard put it - as a finger on the device does.
420
+ */
421
+ function treatMouseAsTouch() {
422
+ if (mouseHooked || typeof document === 'undefined')
423
+ return;
424
+ mouseHooked = true;
425
+ document.addEventListener('mousedown', event => {
426
+ if (!onWeb)
427
+ return;
428
+ const target = event.target;
429
+ if (target?.closest?.('input, textarea, .picker-field__icon, .custom-keyboard')) {
430
+ event.preventDefault();
431
+ // A finger focuses the field it touches, and only that focus brings the keyboard when a
432
+ // calendar was open; preventDefault took it from the mouse - so focus the field here.
433
+ if ((target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) && document.activeElement !== target) {
434
+ window.setTimeout(() => target.focus({ preventScroll: true }));
435
+ }
436
+ }
437
+ }, true);
438
+ }
439
+ /** Whether fields get the toolkit keyboards: on the devices always, in a browser when switched on. */
440
+ function toolkitKeyboardActive() {
441
+ return nativeDevice() || onWeb;
442
+ }
443
+
444
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-16
445
+ // - change: the keyboard opens - and docks at the side in landscape - where toolkitKeyboardActive()
446
+ // says so (also in the Designer preview)
398
447
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
399
448
  // - new: keyboard and calendar take the background colour of the page behind them
400
449
  // (--vl-page-background; a project may set --vl-keyboard-background instead)
@@ -472,8 +521,8 @@ const SIDE_PANEL_WIDTH = 320;
472
521
  * distance to the screen edge), or null for the bottom. Also used by the app to place the dialog.
473
522
  */
474
523
  function sideDockWidth() {
475
- const platform = Capacitor.getPlatform();
476
- if (!Capacitor.isNativePlatform() || (platform !== 'ios' && platform !== 'android')) {
524
+ // on the devices and in the Designer preview with Mobile/Tablet (toolkitKeyboardActive)
525
+ if (!toolkitKeyboardActive()) {
477
526
  return null;
478
527
  }
479
528
  const width = window.innerWidth;
@@ -551,7 +600,7 @@ class CustomKeyboardService {
551
600
  !target.closest('.model-datepicker-panel')) {
552
601
  this.pickerCloser();
553
602
  }
554
- if (!this.isNativeMobile() ||
603
+ if (!toolkitKeyboardActive() ||
555
604
  target.disabled ||
556
605
  this.pickerOverlayOpen ||
557
606
  Date.now() < this.openingSuspendedUntil) {
@@ -1458,6 +1507,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
1458
1507
  }]
1459
1508
  }], ctorParameters: () => [] });
1460
1509
 
1510
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-16
1511
+ // - change: active where toolkitKeyboardActive() says so (also in the Designer preview)
1461
1512
  class CustomKeyboardDirective {
1462
1513
  elementRef;
1463
1514
  keyboard;
@@ -1486,7 +1537,7 @@ class CustomKeyboardDirective {
1486
1537
  openForEvent(event) {
1487
1538
  const type = normalizeCustomKeyboardType(this.keyboardType);
1488
1539
  if (!type ||
1489
- !this.keyboard.isNativeMobile()) {
1540
+ !toolkitKeyboardActive()) {
1490
1541
  return;
1491
1542
  }
1492
1543
  const target = this.resolveInput(event.target);
@@ -1531,6 +1582,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
1531
1582
  args: ['focusin', ['$event']]
1532
1583
  }] } });
1533
1584
 
1585
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-15
1586
+ // - new: backendUrl - the one place that decides how a backend is reached. On the devices the
1587
+ // backend is called directly (native HTTP, no CORS). In the web a backend without CORS headers
1588
+ // (P5 / WildFly) cannot be called from the page, so the call goes to the page's own origin as
1589
+ // /api/proxy/<host>/<path> and the server that serves the app forwards it - only to declared
1590
+ // hosts (locally ng serve's proxy.conf.json, in operation nginx with an allow-list). Replaces the
1591
+ // hard-coded /p5ws path of the web and the direct calls that failed with CORS
1592
+ /** Prefix of the forwarding path on the app's own origin. */
1593
+ const BACKEND_PROXY_PREFIX = '/api/proxy';
1594
+ /**
1595
+ * The address to call for a backend URL: unchanged on the devices and for relative URLs; in the web
1596
+ * https://p5w1.wilken.de/p5ws/x?y=1 becomes <origin>/api/proxy/p5w1.wilken.de/p5ws/x?y=1.
1597
+ */
1598
+ function backendUrl(url) {
1599
+ if (isNativeMobile() || typeof window === 'undefined' || !/^https?:\/\//i.test(url || '')) {
1600
+ return url;
1601
+ }
1602
+ const target = new URL(url);
1603
+ if (target.origin === window.location.origin) {
1604
+ return url;
1605
+ }
1606
+ return `${window.location.origin}${BACKEND_PROXY_PREFIX}/${target.host}${target.pathname}${target.search}`;
1607
+ }
1608
+
1609
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-15
1610
+ // - change: saveDb and getSaveOrder (web) go through backendUrl (/api/proxy/<host>/...) instead of
1611
+ // a direct call (CORS) or the /p5ws path only ng serve knew
1534
1612
  // Shuaib 2026-09-24
1535
1613
  // - change: getLoginUser requires the selected project's Okta issuer.
1536
1614
  // - new: reject a missing or non-HTTPS issuer before sending the request.
@@ -1642,8 +1720,7 @@ class RequestService {
1642
1720
  'Accept': 'application/json'
1643
1721
  });
1644
1722
  let url2 = `${userInfo.p5_backend_url}/assetsmobileservice/Zeiterfassung/registerTimeRecording?company=${userInfo.p5_group}/${userInfo.p5_client}/${userInfo.p5_plant ? userInfo.p5_plant : " "}&appVersion=1&resourceNo=${userInfo.p5_resource_no}`;
1645
- console.log('userInfo: ', userInfo);
1646
- this.http.post(url2, order, { headers }).subscribe({
1723
+ this.http.post(backendUrl(url2), order, { headers }).subscribe({
1647
1724
  next: res => {
1648
1725
  console.log('Save-res: ', res);
1649
1726
  }
@@ -1665,19 +1742,8 @@ class RequestService {
1665
1742
  const company = userInfo.p5_plant
1666
1743
  ? `${userInfo.p5_group}/${userInfo.p5_client}/${userInfo.p5_plant}`
1667
1744
  : `${userInfo.p5_group}/${userInfo.p5_client}/`;
1668
- let backendPath;
1669
- try {
1670
- backendPath = new URL(userInfo.p5_backend_url).pathname;
1671
- }
1672
- catch {
1673
- backendPath = userInfo.p5_backend_url;
1674
- }
1675
- backendPath = `/${backendPath}`
1676
- .replace(/\/{2,}/g, '/')
1677
- .replace(/\/+$/, '');
1678
- const url = `${backendPath}` +
1679
- '/assetsmobileservice/Zeiterfassung/' +
1680
- 'requestBookedTimeRecording';
1745
+ const url = backendUrl(`${String(userInfo.p5_backend_url).replace(/\/+$/, '')}` +
1746
+ '/assetsmobileservice/Zeiterfassung/requestBookedTimeRecording');
1681
1747
  const params = new HttpParams()
1682
1748
  .set('resourceNo', userInfo.p5_resource_no || '')
1683
1749
  .set('workDayFrom', fromDate)
@@ -1691,7 +1757,7 @@ class RequestService {
1691
1757
  params
1692
1758
  });
1693
1759
  }
1694
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: RequestService, deps: [{ token: i1$2.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
1760
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: RequestService, deps: [{ token: i1$1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
1695
1761
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: RequestService, providedIn: 'root' });
1696
1762
  }
1697
1763
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: RequestService, decorators: [{
@@ -1699,7 +1765,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
1699
1765
  args: [{
1700
1766
  providedIn: 'root'
1701
1767
  }]
1702
- }], ctorParameters: () => [{ type: i1$2.HttpClient }] });
1768
+ }], ctorParameters: () => [{ type: i1$1.HttpClient }] });
1703
1769
 
1704
1770
  class UtilityService {
1705
1771
  platform;
@@ -1967,7 +2033,9 @@ class UtilityService {
1967
2033
  authParams: {
1968
2034
  pkce: true,
1969
2035
  issuer: environment.login.web.issuer,
1970
- scopes: environment.login.web.scopes
2036
+ // the form stores login.web.scope; "scopes" was read here and never written, so Okta
2037
+ // web fell back to its own default
2038
+ scopes: this.oauthScopeList(environment.login.web.scopes ?? environment.login.web.scope)
1971
2039
  }
1972
2040
  };
1973
2041
  }
@@ -1982,6 +2050,12 @@ class UtilityService {
1982
2050
  pkceEnabled: environment.login.mobile.pkceEnabled
1983
2051
  };
1984
2052
  }
2053
+ /** OAuth scopes as the list okta-auth-js expects; undefined lets it use its default. */
2054
+ oauthScopeList(scope) {
2055
+ const value = Array.isArray(scope) ? scope.join(' ') : scope;
2056
+ const list = (value || '').split(/\s+/).filter(Boolean);
2057
+ return list.length ? list : undefined;
2058
+ }
1985
2059
  /** OAuth scope as the space separated string the OAuth2 plugin expects. */
1986
2060
  oauthScope(scope) {
1987
2061
  const value = Array.isArray(scope) ? scope.join(' ') : scope;
@@ -2296,6 +2370,10 @@ function calendarLocale(languageCode) {
2296
2370
  };
2297
2371
  }
2298
2372
 
2373
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-16
2374
+ // - fix: with a mouse the calendar/time picker opens after the click - the click closed it at once
2375
+ // - change: docked like on the devices wherever toolkitKeyboardActive() (also in the Designer preview);
2376
+ // the resize listener is always there and does nothing without a docked panel
2299
2377
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
2300
2378
  // - new: the docked calendar takes the page's background colour, like the keyboard
2301
2379
  // - new: the calendar follows the project language (calendarLocale, from Intl); it was English
@@ -2341,12 +2419,15 @@ const TIME_PANEL_ROOM = 104;
2341
2419
  class DateTimePickerService {
2342
2420
  keyboard;
2343
2421
  /** On the devices the date picker docks at the bottom like a keyboard; in a browser it drops down. */
2344
- isNativeMobile = Capacitor.isNativePlatform() &&
2345
- (Capacitor.getPlatform() === 'ios' || Capacitor.getPlatform() === 'android');
2422
+ get isNativeMobile() {
2423
+ return toolkitKeyboardActive();
2424
+ }
2346
2425
  /** panelStyleClass of a date picker. */
2347
- datePanelClass = this.isNativeMobile
2348
- ? 'model-datepicker-panel model-datepicker-panel--docked'
2349
- : 'model-datepicker-panel';
2426
+ get datePanelClass() {
2427
+ return this.isNativeMobile
2428
+ ? 'model-datepicker-panel model-datepicker-panel--docked'
2429
+ : 'model-datepicker-panel';
2430
+ }
2350
2431
  /** panelStyleClass of a time picker: compact and attached to its field everywhere. */
2351
2432
  timePanelClass = 'model-datepicker-panel model-timepicker-panel';
2352
2433
  /**
@@ -2354,7 +2435,9 @@ class DateTimePickerService {
2354
2435
  * wrapper around the panel, and a transformed wrapper becomes the reference of the panel's
2355
2436
  * position: fixed - the docked calendar flew up next to its field while closing.
2356
2437
  */
2357
- dockedMotion = this.isNativeMobile ? { disabled: true } : undefined;
2438
+ get dockedMotion() {
2439
+ return this.isNativeMobile ? { disabled: true } : undefined;
2440
+ }
2358
2441
  activePicker = null;
2359
2442
  activePickerRequest = null;
2360
2443
  pickerOpening = false;
@@ -2371,7 +2454,7 @@ class DateTimePickerService {
2371
2454
  }
2372
2455
  });
2373
2456
  // Turning the device with the docked calendar open: measure and line up again.
2374
- if (this.isNativeMobile) {
2457
+ {
2375
2458
  window.addEventListener('resize', () => {
2376
2459
  const docked = document.querySelector('.model-datepicker-panel--docked');
2377
2460
  if (!docked) {
@@ -2383,7 +2466,8 @@ class DateTimePickerService {
2383
2466
  });
2384
2467
  }
2385
2468
  this.keyboard.setPickerCloser(() => this.closeActivePicker(undefined, { suspendReopen: false, keepBottomArea: true }));
2386
- if (this.isNativeMobile) {
2469
+ // the system keyboard exists only on the device - not in the preview (plugin missing on web)
2470
+ if (Capacitor.isNativePlatform()) {
2387
2471
  void Keyboard.addListener('keyboardWillShow', () => (this.systemKeyboardVisible = true));
2388
2472
  void Keyboard.addListener('keyboardDidHide', () => (this.systemKeyboardVisible = false));
2389
2473
  }
@@ -2537,6 +2621,19 @@ class DateTimePickerService {
2537
2621
  })
2538
2622
  ]);
2539
2623
  }
2624
+ // With a mouse the click follows the pointerdown and counts for PrimeNG as a click outside:
2625
+ // a picker opened now was closed at once. Open it after that click (or after 600 ms).
2626
+ if (!Capacitor.isNativePlatform() && event.pointerType === 'mouse') {
2627
+ await new Promise(resolve => {
2628
+ const done = () => {
2629
+ document.removeEventListener('click', done, true);
2630
+ window.clearTimeout(timer);
2631
+ window.setTimeout(resolve);
2632
+ };
2633
+ const timer = window.setTimeout(done, 600);
2634
+ document.addEventListener('click', done, true);
2635
+ });
2636
+ }
2540
2637
  if (Capacitor.isNativePlatform()) {
2541
2638
  // Let the layout settle after the keyboard - system or toolkit - has gone.
2542
2639
  await new Promise(resolve => {
@@ -2832,7 +2929,7 @@ class NotificationService {
2832
2929
  error(error, time = 20000, message) {
2833
2930
  this.toastr.error(error, message, { messageClass: 'toaster-font', titleClass: 'toaster-font', closeButton: true, timeOut: time });
2834
2931
  }
2835
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NotificationService, deps: [{ token: i1$3.ToastrService }], target: i0.ɵɵFactoryTarget.Injectable });
2932
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NotificationService, deps: [{ token: i1$2.ToastrService }], target: i0.ɵɵFactoryTarget.Injectable });
2836
2933
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NotificationService, providedIn: 'root' });
2837
2934
  }
2838
2935
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NotificationService, decorators: [{
@@ -2840,7 +2937,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
2840
2937
  args: [{
2841
2938
  providedIn: 'root'
2842
2939
  }]
2843
- }], ctorParameters: () => [{ type: i1$3.ToastrService }] });
2940
+ }], ctorParameters: () => [{ type: i1$2.ToastrService }] });
2844
2941
 
2845
2942
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
2846
2943
  // - new: RequestTimeoutService / stagedRequest, one waiting rule for every request of the runtime
@@ -2935,6 +3032,9 @@ function stagedRequest(work) {
2935
3032
  return service ? service.run(work) : work(new AbortController().signal);
2936
3033
  }
2937
3034
 
3035
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-15
3036
+ // - change: the web autocomplete goes through backendUrl (/api/proxy/<host>/...) instead of the
3037
+ // /p5ws path only ng serve forwarded
2938
3038
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
2939
3039
  // - new: RemoteListService, the one place that loads a field's list from the backend
2940
3040
  // (option.autocomplete: url, method, params with {{userInfo.x}} and company), under the waiting
@@ -3013,8 +3113,8 @@ class RemoteListService {
3013
3113
  : {}
3014
3114
  };
3015
3115
  }
3016
- getAutocompleteRequestUrl(backendUrl, endpoint) {
3017
- const normalizedBackendUrl = backendUrl?.trim()?.replace(/\/+$/, '');
3116
+ getAutocompleteRequestUrl(backendUrl$1, endpoint) {
3117
+ const normalizedBackendUrl = backendUrl$1?.trim()?.replace(/\/+$/, '');
3018
3118
  const normalizedEndpoint = endpoint?.trim();
3019
3119
  if (!normalizedBackendUrl) {
3020
3120
  throw new Error('Missing p5_backend_url for autocomplete request');
@@ -3029,30 +3129,11 @@ class RemoteListService {
3029
3129
  return (`${normalizedBackendUrl}/` +
3030
3130
  normalizedEndpoint.replace(/^\/+/, ''));
3031
3131
  }
3032
- /*
3033
- * Web:
3034
- * Return a same-origin path beginning with /p5ws.
3035
- * Angular's development proxy will forward it.
3036
- */
3037
- if (/^https?:\/\//i.test(normalizedEndpoint)) {
3038
- const absoluteEndpoint = new URL(normalizedEndpoint);
3039
- return (absoluteEndpoint.pathname +
3040
- absoluteEndpoint.search);
3041
- }
3042
- let backendPath;
3043
- try {
3044
- backendPath =
3045
- new URL(normalizedBackendUrl).pathname;
3046
- }
3047
- catch {
3048
- backendPath =
3049
- normalizedBackendUrl;
3050
- }
3051
- backendPath = `/${backendPath}`
3052
- .replace(/\/{2,}/g, '/')
3053
- .replace(/\/+$/, '');
3054
- const endpointPath = normalizedEndpoint.replace(/^\/+/, '');
3055
- return (`${backendPath}/${endpointPath}`);
3132
+ // Web: through the app's own origin (/api/proxy/<host>/...), P5 sends no CORS headers
3133
+ const absolute = /^https?:\/\//i.test(normalizedEndpoint)
3134
+ ? normalizedEndpoint
3135
+ : `${normalizedBackendUrl}/${normalizedEndpoint.replace(/^\/+/, '')}`;
3136
+ return backendUrl(absolute);
3056
3137
  }
3057
3138
  buildCompany(userInfo) {
3058
3139
  return userInfo.p5_plant
@@ -3085,6 +3166,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
3085
3166
  }]
3086
3167
  }] });
3087
3168
 
3169
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-15
3170
+ // - new (#145): media stored as a CouchDB attachment (type media, storage attachment, no data).
3171
+ // getFileContentFromCouchDb answers it with src = a signed link (getMediaStreamUrl: POST
3172
+ // /media-token with the tenant header) the player streams with Range through the media cache.
3173
+ // resolveMediaSource turns a file name in a media field into that link. This brings back what
3174
+ // getMediaUrlFromCouchDb did, without a login token: /media-token asks for the tenant header only
3175
+ // - new (#145): preloadProjectMedia - at the start the media named in environments/media.json (the
3176
+ // Designer's list of media the pages use) get their links, files up to 20 MB are loaded whole
3177
+ // (object URL): Play starts at once; resolveMediaSource uses them
3178
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-14
3179
+ // - new: listFiles(dbName, prefix) - the files of a project under a folder, e.g. ssl/mobile/, so
3180
+ // the app can find its certificate instead of expecting a fixed file name
3088
3181
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
3089
3182
  // - change: the CouchDB reads run under the waiting rule (stagedRequest), so an unreachable CouchDB
3090
3183
  // no longer leaves the app loading for ever
@@ -3172,6 +3265,20 @@ class Couchdb {
3172
3265
  console.error(`Failed to fetch file "${docId}" from "${dbName}": ${response.status}`, doc);
3173
3266
  return null;
3174
3267
  }
3268
+ // A media file stored as an attachment (#145): no data, the bytes stay in CouchDB. The
3269
+ // src is a signed link the player reads itself with Range - streamed through the cache
3270
+ if (!doc?.data && this.isAttachmentMedia(doc)) {
3271
+ const name = doc.name || docId.split('/').pop();
3272
+ const mimeType = this.getMimeType(name, doc.contentType);
3273
+ return {
3274
+ type: 'media',
3275
+ mimeType,
3276
+ src: await this.getMediaStreamUrl(dbName, docId, name),
3277
+ name,
3278
+ path: doc.path,
3279
+ metadata: doc.metadata || {},
3280
+ };
3281
+ }
3175
3282
  if (!doc?.data) {
3176
3283
  console.error(`Document "${docId}" does not contain file data`);
3177
3284
  return null;
@@ -3320,12 +3427,128 @@ class Couchdb {
3320
3427
  '.vtt', '.srt',
3321
3428
  ].some((extension) => lower.endsWith(extension));
3322
3429
  }
3430
+ /** Ids of the files under a folder of the project, e.g. listFiles(db, 'ssl/mobile/'). Empty on any error. */
3431
+ async listFiles(dbName, prefix) {
3432
+ try {
3433
+ const range = `startkey=${encodeURIComponent(JSON.stringify(prefix))}` +
3434
+ `&endkey=${encodeURIComponent(JSON.stringify(`${prefix}\ufff0`))}`;
3435
+ const response = await stagedRequest(async (signal) => fetch(`${this.getDbUrl(dbName)}/_all_docs?${range}`, { signal,
3436
+ method: 'GET',
3437
+ headers: await this.getReadHeaders(),
3438
+ }));
3439
+ if (!response.ok)
3440
+ return [];
3441
+ const body = await response.json();
3442
+ return (body?.rows ?? []).map((row) => String(row.id));
3443
+ }
3444
+ catch (error) {
3445
+ console.error(`Failed to list "${prefix}" in "${dbName}"`, error);
3446
+ return [];
3447
+ }
3448
+ }
3323
3449
  getDocumentUrl(dbName, docId) {
3324
3450
  return `${this.getDbUrl(dbName)}/${encodeURIComponent(docId)}`;
3325
3451
  }
3326
3452
  getDbUrl(dbName) {
3327
3453
  return `${this.couchDbBaseUrl}/${dbName}`;
3328
3454
  }
3455
+ isAttachmentMedia(doc) {
3456
+ const name = doc?.name || String(doc?._id || '').split('/').pop();
3457
+ return doc?.storage === 'attachment' && !!doc?._attachments?.[name];
3458
+ }
3459
+ /** Signed links by "db/doc/name", reused until a minute before they run out. */
3460
+ mediaLinks = new Map();
3461
+ /**
3462
+ * A signed link for one media attachment (#145). A <video>/<audio src> sends no tenant header,
3463
+ * so POST /media-token is asked with it instead - the same header every read already carries,
3464
+ * nothing else is needed - and answers the path it signed plus a token for that one file
3465
+ * (8 hours). The player then reads couchdb.yomotech.de<path>?token=... with Range itself.
3466
+ */
3467
+ async getMediaStreamUrl(dbName, docId, name) {
3468
+ const key = `${dbName}/${docId}/${name}`;
3469
+ const known = this.mediaLinks.get(key);
3470
+ if (known && known.until > Date.now())
3471
+ return known.url;
3472
+ const response = await stagedRequest(async (signal) => fetch(`${this.couchDbBaseUrl}/media-token`, { signal,
3473
+ method: 'POST',
3474
+ headers: { ...(await this.getReadHeaders()), 'Content-Type': 'application/json' },
3475
+ body: JSON.stringify({ db: dbName, path: docId, name }),
3476
+ }));
3477
+ const body = await this.parseJsonOrText(response);
3478
+ if (!response.ok) {
3479
+ throw new Error(`media-token for "${docId}" in "${dbName}": ${response.status} ${this.stringifyErrorDetails(body)}`);
3480
+ }
3481
+ const url = `${this.couchDbBaseUrl}${body.path}?token=${encodeURIComponent(body.token)}`;
3482
+ this.mediaLinks.set(key, { url, until: (Number(body.expiresAt) - 60) * 1000 });
3483
+ return url;
3484
+ }
3485
+ /**
3486
+ * What a media field holds, made playable: a URL (http, https, data, blob) stays as it is; a
3487
+ * file name ("clip.mp4", also "Media/clip.mp4") is read from Media/ of the selected project -
3488
+ * a signed link for an attachment, a data URL for an older Base64 document. '' when unknown.
3489
+ */
3490
+ async resolveMediaSource(source) {
3491
+ const value = (source || '').trim();
3492
+ if (!value || /^(https?:|data:|blob:|capacitor:|file:)/i.test(value) || value.startsWith('/'))
3493
+ return value;
3494
+ const native = isNativeMobile();
3495
+ const tenantName = native ? await this.utilityService.getLocalStorage('tenantName') : await this.utilityService.getStorage('tenantName');
3496
+ const project = native ? await this.utilityService.getLocalStorage('selectedProject') : await this.utilityService.getStorage('selectedProject');
3497
+ if (!tenantName || !project)
3498
+ return '';
3499
+ const dbName = `${tenantName}_${project}`;
3500
+ const name = value.split('/').pop() || value;
3501
+ const preloaded = this.preloadedMedia.get(`${dbName}/${name}`);
3502
+ if (preloaded) {
3503
+ const url = await preloaded.catch(() => '');
3504
+ if (url)
3505
+ return url;
3506
+ }
3507
+ const file = await this.getFileContentFromCouchDb(dbName, `Media/${name}`);
3508
+ return this.utilityService.getMediaSrc(file);
3509
+ }
3510
+ /** Media prepared at the start, by "db/name": an object URL (loaded whole) or a signed link. */
3511
+ preloadedMedia = new Map();
3512
+ /**
3513
+ * Prepares the project's video and audio at the start of the app, in the background (#145): only
3514
+ * the files in environments/media.json - the media the pages use, written by the Designer on
3515
+ * export; without that list nothing is preloaded. Per file the signed link; a file up to maxBytes
3516
+ * (20 MB) is loaded whole and kept as an object URL, so Play starts at once and without network. A
3517
+ * larger one keeps only the link. A failure is no error for the app: resolveMediaSource then asks
3518
+ * for the link as before.
3519
+ */
3520
+ async preloadProjectMedia(dbName, maxBytes = 20 * 1024 * 1024) {
3521
+ const list = await this.getFileContentFromCouchDb(dbName, 'environments/media.json');
3522
+ const names = Array.isArray(list?.preload) ? list.preload.filter((n) => typeof n === 'string' && n) : [];
3523
+ for (const name of names) {
3524
+ const key = `${dbName}/${name}`;
3525
+ if (this.preloadedMedia.has(key))
3526
+ continue;
3527
+ const docId = `Media/${name}`;
3528
+ const pending = (async () => {
3529
+ const response = await stagedRequest(async (signal) => fetch(this.getDocumentUrl(dbName, docId), { signal,
3530
+ method: 'GET',
3531
+ headers: await this.getReadHeaders(),
3532
+ }));
3533
+ const doc = response.ok ? await response.json() : null;
3534
+ if (!this.isAttachmentMedia(doc))
3535
+ throw new Error(`${docId} is no media attachment`);
3536
+ const link = await this.getMediaStreamUrl(dbName, docId, name);
3537
+ const size = Number(doc._attachments?.[name]?.length || 0);
3538
+ if (!size || size > maxBytes)
3539
+ return link;
3540
+ const bytes = await fetch(link);
3541
+ if (!bytes.ok)
3542
+ return link;
3543
+ return URL.createObjectURL(await bytes.blob());
3544
+ })();
3545
+ pending.catch(error => {
3546
+ console.warn(`Media "${name}" not preloaded`, error);
3547
+ this.preloadedMedia.delete(key);
3548
+ });
3549
+ this.preloadedMedia.set(key, pending);
3550
+ }
3551
+ }
3329
3552
  async getReadHeaders() {
3330
3553
  return {
3331
3554
  Accept: 'application/json',
@@ -3385,6 +3608,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
3385
3608
  }]
3386
3609
  }], ctorParameters: () => [{ type: UtilityService }, { type: i1.Platform }] });
3387
3610
 
3611
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-14
3612
+ // - fix: Keycloak on iOS/Android came from a hard-coded block (realm p5, client "mobile",
3613
+ // de.yomotech:/login/callback) instead of login.mobile, so every project signed in at wilken/p5
3614
+ // and login.mobile.offlineAccess had no effect. Now login.mobile of the project is used
3615
+ // - new: the Keycloak web scope comes from login.web.scope instead of a fixed "openid profile email"
3616
+ // - new: the web takes login.web.redirectUri and login.web.logoutRedirectUri, the origin otherwise
3388
3617
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-11
3389
3618
  // - fix: "is this the app on iOS/Android" asks isNativeMobile() (native-platform.ts) instead of
3390
3619
  // Angular CDK platform.IOS/ANDROID; an iPad reports itself as a Mac there, so it ran as the web
@@ -3487,22 +3716,29 @@ class ConfigService {
3487
3716
  }
3488
3717
  if (env?.environment?.login.type === "keycloak") {
3489
3718
  if (isNativeMobile()) {
3719
+ // login.mobile of the project - every project its own realm and client
3720
+ const mobile = env?.environment?.login?.mobile ?? {};
3490
3721
  this.keycloakConfig = {
3491
- issuer: "https://keycloak-poc.wilken.de/realms/p5",
3492
- clientId: "mobile",
3493
- redirectUri: "de.yomotech:/login/callback",
3494
- logoutRedirectUri: "de.yomotech:/login/callback",
3495
- scope: "openid profile email",
3496
- responseType: "code"
3722
+ issuer: mobile.issuer,
3723
+ clientId: mobile.clientId,
3724
+ redirectUri: mobile.redirectUri,
3725
+ logoutRedirectUri: mobile.logoutRedirectUri,
3726
+ scope: mobile.scope || "openid profile email",
3727
+ responseType: "code",
3728
+ offlineAccess: mobile.offlineAccess === true
3497
3729
  };
3498
3730
  }
3499
3731
  else {
3732
+ const web = env?.environment?.login?.web ?? {};
3500
3733
  this.keycloakConfig = {
3501
- issuer: env?.environment?.login?.web?.issuer ?? 'http://dummy',
3502
- clientId: env?.environment?.login?.web?.clientId ?? 'dummy',
3503
- redirectUri: window.location.origin,
3734
+ issuer: web.issuer ?? 'http://dummy',
3735
+ clientId: web.clientId ?? 'dummy',
3736
+ // login.web.redirectUri / logoutRedirectUri; the origin is right for most web builds
3737
+ redirectUri: web.redirectUri || window.location.origin,
3738
+ postLogoutRedirectUri: web.logoutRedirectUri || window.location.origin,
3504
3739
  responseType: 'code',
3505
- scope: 'openid profile email',
3740
+ // login.web.scope - a list with one space separated string, or a string
3741
+ scope: (Array.isArray(web.scope) ? web.scope.join(' ') : web.scope || '').trim() || 'openid profile email',
3506
3742
  showDebugInformation: true
3507
3743
  };
3508
3744
  }
@@ -4424,8 +4660,8 @@ class CardComponent {
4424
4660
  }
4425
4661
  return 'image';
4426
4662
  }
4427
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CardComponent, deps: [{ token: UtilityService }, { token: i1.Platform }, { token: i1$2.HttpClient }, { token: ConfigService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i5.NzModalService }, { token: NotificationService }, { token: i7.OAuthService }, { token: RequestService }], target: i0.ɵɵFactoryTarget.Component });
4428
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: CardComponent, isStandalone: true, selector: "vl-card", inputs: { itemInfo: "itemInfo", currentPage: "currentPage", pageSize: "pageSize", selectedLanguageInfo: "selectedLanguageInfo", mobileDisplay: "mobileDisplay", scrollPagination: "scrollPagination", pages: "pages", paginationMode: "paginationMode", paginationLoading: "paginationLoading" }, outputs: { cardButtonClick: "cardButtonClick", cardHeaderButtonClick: "cardHeaderButtonClick", linkDetails: "linkDetails", imageDetails: "imageDetails", onPageSizeChange: "onPageSizeChange", onPageChange: "onPageChange", cardDataChange: "cardDataChange" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "scrollPaginationSentinel", first: true, predicate: ["scrollPaginationSentinel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the bottom pagination read itemInfo?.data.length; before the records arrive data is\r\n undefined, the template threw and the cards stayed half drawn (luv2shop start page: one\r\n card without texts)\r\n - fix: the price fields read ...?.format.currency; a property without format threw the same\r\n way (luv2shop: no prices, cards cut off) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: the date filter uses the shared picker markup (picker-field, DateTimePickerService) -->\r\n\r\n<!-- shuaib 2026-09-24\r\n - change: card field switches use renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment naming the types this container does not render. It carries four\r\n ngSwitch blocks, so the gap belongs to the container and not to one of them -->\r\n\r\n<!-- @contract gap: dateRange, quantity, credit card\r\n reality: a card renders string, number, image, date, time, dropdown, boolean and\r\n autocomplete. Legacy checkbox fields normalize to boolean. dropdown and boolean arrived on 2026-09-18; before that a card\r\n dropped them silently.\r\n failure: a field of a missing type renders empty, with no error.\r\n instead: quantity is grid-only by design; for a dateRange or a credit card use a modal.\r\n seen: this file carries four switch blocks (lines 23, 94, 267, 488) and none of them has\r\n a case for those three - the gap belongs to the container, not to one block -->\r\n\r\n<!-- Loader Spinner -->\r\n\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div class=\"vl-card-runtime\">\r\n <!-- top pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n itemInfo?.layoutSchema?.pagination?.top\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n <div *ngIf=\"itemInfo?.header?.elements?.length\" [ngStyle]=\"getHeaderStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.header?.style\r\n ).classes\">\r\n <div *ngFor=\"let element of itemInfo.header?.elements; let c = index\"\r\n [ngStyle]=\"getHeaderElementWrapperStyle(element)\"\r\n [class]=\"element.option?.innerAction && element.option?.innerAction === 'saveAll' && !displayedRecords?.length ? \r\n 'hidden' :getStyleWithoutClasses(element?.wrapperStyle).classes\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"element.option?.visible ? 'card-field-wrapper' : 'hidden'\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"element.label?.caption\"\r\n [style]=\"getStyleWithoutClasses(element.label?.style).style\"\r\n [class]=\"element.label?.style?.classes?.[0]\">\r\n {{ element.label.caption }}\r\n </span>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <vl-string [formField]=\"element\"\r\n [modalItemInfo]=\"itemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\"\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : ''\"\r\n [ngClass]=\"element?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <div class=\"picker-field\">\r\n <p-datepicker #cardDatePicker\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [dateFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].format : 'yy-mm-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [class]=\"element?.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"element?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, cardDatePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(cardDatePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, cardDatePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardHeader(element)\">\r\n <button nz-button type=\"button\"\r\n [matTooltip]=\"element?.option?.tooltip || ''\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [ngStyle]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else headerTextContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #headerTextContent>{{ element.caption\r\n }}</ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <div class=\"card-responsive-grid\" [ngStyle]=\"getCardContainerStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.layoutSchema?.style\r\n ).classes\">\r\n <div *ngFor=\"let record of displayedRecords; let k = index\"\r\n class=\"card-grid-item\">\r\n <div [ngStyle]=\"getSingleCardStyle()\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.layoutSchema?.stylePerCard).classes\">\r\n <div *ngFor=\"let layout of itemInfo?.layoutSchema?.cards || []\"\r\n class=\"card-layout-item\" [ngStyle]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? {}\r\n : getStyleWithoutClasses(layout?.style).style\r\n \" [class]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? ''\r\n : getStyleWithoutClasses(layout?.style).classes\r\n \">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(layout?.scope?.reference && itemInfo.propertySchema[getScope(layout.scope?.reference)] ? itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.type : layout?.type == 'HorizontalLayout' ? 'HorizontalLayout' : 'default')\">\r\n <!-- Media Control -->\r\n <div\r\n *ngSwitchCase=\"'media'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <ng-container\r\n *ngTemplateOutlet=\"mediaContent; context: { layout: layout, record: record }\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(layout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout.label?.style).classes\">\r\n {{ getHorizontalLayoutCaption(layout) }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(layout.option && layout.option.control &&\r\n !layout.option.protected){\r\n @switch (layout.option.control) {\r\n @case ('textInput') {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[layout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, layout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: layout.scope?.reference\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <!-- VALUE -->\r\n @if(layout.option && layout.option.control &&\r\n !layout.option.protected){\r\n @switch (layout.option.control) {\r\n @case ('textInput') {\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n }\r\n @case ('slider') {\r\n <p>Slider not Implemented yet</p>\r\n\r\n }\r\n @case ('spinner') {\r\n <p>Spinner not Implemented yet</p>\r\n }\r\n }\r\n } @else{\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n }\r\n\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[layout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, layout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: layout.scope?.reference\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"layout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <button\r\n [style]=\"getStyleWithoutClasses(layout?.style).style\"\r\n [class]=\"layout.option.protected ? 'disabled-button' : getStyleWithoutClasses(layout?.style).classes\"\r\n (click)=\"\r\n invokeButtonClick(\r\n layout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <ng-container\r\n *ngIf=\"!layout.caption; else textContent\">\r\n <i nz-icon [nzType]=\"layout.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ layout.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n\r\n <!-- HorizontalLayout Control -->\r\n <div *ngSwitchCase=\"'HorizontalLayout'\">\r\n <div class=\"card-horizontal-layout\"\r\n [class]=\"getStyleWithoutClasses(\r\n layout?.style\r\n ).classes\" [ngStyle]=\"getHorizontalLayoutStyle(layout)\">\r\n <ng-container\r\n *ngFor=\"let innerLayout of layout.elements\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(innerLayout.scope?.reference && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] ? itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n <!-- Media Control -->\r\n <div\r\n *ngSwitchCase=\"'media'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n\r\n <ng-container\r\n *ngTemplateOutlet=\"mediaContent; context: {\r\n layout: innerLayout,\r\n record: record\r\n }\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(innerLayout.option &&\r\n innerLayout.option.control &&\r\n !innerLayout.option.protected){\r\n @switch\r\n (innerLayout.option.control)\r\n {\r\n @case ('textInput') {\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template\r\n #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template\r\n #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported\r\n yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported\r\n yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported\r\n yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported\r\n yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported\r\n yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[innerLayout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, innerLayout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: innerLayout.scope?.reference\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else{\r\n\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template\r\n #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template\r\n #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n {{\r\n record[innerLayout.scope?.reference.split('/').pop()]\r\n | currency:\r\n innerLayout.option?.currency\r\n ?\r\n innerLayout.option.currency\r\n :\r\n 'USD'\r\n }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.dateFormat\r\n ?\r\n innerLayout.option?.dateFormat\r\n :\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'yyyy-MM-dd') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.timeFormat\r\n ?\r\n innerLayout.option?.timeFormat\r\n :itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'HH:mm') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <select\r\n id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[innerLayout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, innerLayout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: innerLayout.scope?.reference\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"innerLayout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button nz-button\r\n [matTooltip]=\"getHorizontalLayoutTooltip(innerLayout)\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.style).style\"\r\n [class]=\"innerLayout.option?.visible ? innerLayout.option.protected ? 'disabled-button' : getStyleWithoutClasses(innerLayout?.style).classes : 'hidden'\"\r\n (click)=\"\r\n invokeButtonClick(\r\n innerLayout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <span\r\n class=\"schema-btn-content\">\r\n <i\r\n *ngIf=\"showButtonIcon(innerLayout)\"\r\n nz-icon\r\n [nzType]=\"innerLayout.icon\"\r\n nzTheme=\"outline\">\r\n </i>\r\n\r\n <span\r\n *ngIf=\"showButtonCaption(innerLayout)\">\r\n {{\r\n innerLayout.caption\r\n }}\r\n </span>\r\n\r\n <span *ngIf=\"\r\n !showButtonIcon(innerLayout) &&\r\n !showButtonCaption(innerLayout)\r\n \">\r\n Button\r\n </span>\r\n </span>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div\r\n *ngIf=\"itemInfo?.footer?.elements && itemInfo?.data && itemInfo?.data.length\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.footer?.style).classes\">\r\n <span\r\n *ngFor=\"let element of itemInfo.footer?.elements; let c = index\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <h5>{{\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\">\r\n <div\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value :\r\n element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-date-picker\r\n [required]=\"element.option.required\"\r\n [name]=\"element.scope?.reference\"\r\n [nzDisabled]=\"element?.option?.disabled\"\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [nzFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format : 'yyyy-MM-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\"></nz-date-picker>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardFooter(element)\">\r\n <button nz-button\r\n [style]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else textContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ element.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </span>\r\n </div>\r\n <!-- bottom pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.data?.length && \r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n !itemInfo?.layoutSchema?.pagination?.top &&\r\n !scrollPagination\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\"\r\n [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n\r\n @if (scrollPagination) {\r\n <div #scrollPaginationSentinel class=\"scroll-pagination-sentinel\"\r\n aria-hidden=\"true\">\r\n </div>\r\n }\r\n</div>\r\n<ng-template #mediaContent let-layout=\"layout\" let-record=\"record\">\r\n <ng-container *ngIf=\"getMediaSource(layout, record) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(layout, record)\">\r\n\r\n <!-- Default: image. Keep your existing image-click behaviour. -->\r\n <img\r\n *ngSwitchDefault\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(layout)\"\r\n loading=\"lazy\"\r\n (click)=\"showImageDetails(layout.option, record)\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(layout)\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(layout)\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(layout) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(layout) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>", styles: [":host{display:block;width:100%;min-width:0}.vl-card-runtime{min-width:0;box-sizing:border-box}.card-responsive-grid{width:100%;min-width:0;box-sizing:border-box}.scroll-pagination-sentinel{width:100%;height:1px}.card-grid-item,.card-layout-item,.card-horizontal-layout,.card-field-wrapper{width:100%;min-width:0;box-sizing:border-box}.card-field-wrapper{overflow-wrap:anywhere}.card-field-wrapper p,.card-field-wrapper h3{margin:0;min-width:0}.card-layout-item img{display:block;max-width:100%;height:auto}.card-layout-item button{max-width:100%;box-sizing:border-box}.schema-btn-content{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:0}.hidden{display:none!important}.card-pagination-wrapper{width:100%;min-width:0;box-sizing:border-box;padding:0 10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i13.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i14.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i15.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "ngmodule", type: NzModalModule }, { kind: "component", type: PaginationComponent, selector: "vl-pagination", inputs: ["itemInfo", "pageSize", "currentPage", "selectedLanguageInfo", "pages"], outputs: ["pageSizeChange", "onGoToPage"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i17.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: PlaceholderPipe, name: "vlPlaceholder" }, { kind: "pipe", type: i1$1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] });
4663
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CardComponent, deps: [{ token: UtilityService }, { token: i1.Platform }, { token: i1$1.HttpClient }, { token: ConfigService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i5$1.NzModalService }, { token: NotificationService }, { token: i7.OAuthService }, { token: RequestService }], target: i0.ɵɵFactoryTarget.Component });
4664
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: CardComponent, isStandalone: true, selector: "vl-card", inputs: { itemInfo: "itemInfo", currentPage: "currentPage", pageSize: "pageSize", selectedLanguageInfo: "selectedLanguageInfo", mobileDisplay: "mobileDisplay", scrollPagination: "scrollPagination", pages: "pages", paginationMode: "paginationMode", paginationLoading: "paginationLoading" }, outputs: { cardButtonClick: "cardButtonClick", cardHeaderButtonClick: "cardHeaderButtonClick", linkDetails: "linkDetails", imageDetails: "imageDetails", onPageSizeChange: "onPageSizeChange", onPageChange: "onPageChange", cardDataChange: "cardDataChange" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "scrollPaginationSentinel", first: true, predicate: ["scrollPaginationSentinel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the bottom pagination read itemInfo?.data.length; before the records arrive data is\r\n undefined, the template threw and the cards stayed half drawn (luv2shop start page: one\r\n card without texts)\r\n - fix: the price fields read ...?.format.currency; a property without format threw the same\r\n way (luv2shop: no prices, cards cut off) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: the date filter uses the shared picker markup (picker-field, DateTimePickerService) -->\r\n\r\n<!-- shuaib 2026-09-24\r\n - change: card field switches use renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment naming the types this container does not render. It carries four\r\n ngSwitch blocks, so the gap belongs to the container and not to one of them -->\r\n\r\n<!-- @contract gap: dateRange, quantity, credit card\r\n reality: a card renders string, number, image, date, time, dropdown, boolean and\r\n autocomplete. Legacy checkbox fields normalize to boolean. dropdown and boolean arrived on 2026-09-18; before that a card\r\n dropped them silently.\r\n failure: a field of a missing type renders empty, with no error.\r\n instead: quantity is grid-only by design; for a dateRange or a credit card use a modal.\r\n seen: this file carries four switch blocks (lines 23, 94, 267, 488) and none of them has\r\n a case for those three - the gap belongs to the container, not to one block -->\r\n\r\n<!-- Loader Spinner -->\r\n\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div class=\"vl-card-runtime\">\r\n <!-- top pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n itemInfo?.layoutSchema?.pagination?.top\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n <div *ngIf=\"itemInfo?.header?.elements?.length\" [ngStyle]=\"getHeaderStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.header?.style\r\n ).classes\">\r\n <div *ngFor=\"let element of itemInfo.header?.elements; let c = index\"\r\n [ngStyle]=\"getHeaderElementWrapperStyle(element)\"\r\n [class]=\"element.option?.innerAction && element.option?.innerAction === 'saveAll' && !displayedRecords?.length ? \r\n 'hidden' :getStyleWithoutClasses(element?.wrapperStyle).classes\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"element.option?.visible ? 'card-field-wrapper' : 'hidden'\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"element.label?.caption\"\r\n [style]=\"getStyleWithoutClasses(element.label?.style).style\"\r\n [class]=\"element.label?.style?.classes?.[0]\">\r\n {{ element.label.caption }}\r\n </span>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <vl-string [formField]=\"element\"\r\n [modalItemInfo]=\"itemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\"\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : ''\"\r\n [ngClass]=\"element?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <div class=\"picker-field\">\r\n <p-datepicker #cardDatePicker\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [dateFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].format : 'yy-mm-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [class]=\"element?.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"element?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, cardDatePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(cardDatePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, cardDatePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardHeader(element)\">\r\n <button nz-button type=\"button\"\r\n [matTooltip]=\"element?.option?.tooltip || ''\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [ngStyle]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else headerTextContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #headerTextContent>{{ element.caption\r\n }}</ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <div class=\"card-responsive-grid\" [ngStyle]=\"getCardContainerStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.layoutSchema?.style\r\n ).classes\">\r\n <div *ngFor=\"let record of displayedRecords; let k = index\"\r\n class=\"card-grid-item\">\r\n <div [ngStyle]=\"getSingleCardStyle()\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.layoutSchema?.stylePerCard).classes\">\r\n <div *ngFor=\"let layout of itemInfo?.layoutSchema?.cards || []\"\r\n class=\"card-layout-item\" [ngStyle]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? {}\r\n : getStyleWithoutClasses(layout?.style).style\r\n \" [class]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? ''\r\n : getStyleWithoutClasses(layout?.style).classes\r\n \">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(layout?.scope?.reference && itemInfo.propertySchema[getScope(layout.scope?.reference)] ? itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.type : layout?.type == 'HorizontalLayout' ? 'HorizontalLayout' : 'default')\">\r\n <!-- Media Control -->\r\n <div\r\n *ngSwitchCase=\"'media'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <ng-container\r\n *ngTemplateOutlet=\"mediaContent; context: { layout: layout, record: record }\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(layout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout.label?.style).classes\">\r\n {{ getHorizontalLayoutCaption(layout) }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(layout.option && layout.option.control &&\r\n !layout.option.protected){\r\n @switch (layout.option.control) {\r\n @case ('textInput') {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[layout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, layout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: layout.scope?.reference\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <!-- VALUE -->\r\n @if(layout.option && layout.option.control &&\r\n !layout.option.protected){\r\n @switch (layout.option.control) {\r\n @case ('textInput') {\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n }\r\n @case ('slider') {\r\n <p>Slider not Implemented yet</p>\r\n\r\n }\r\n @case ('spinner') {\r\n <p>Spinner not Implemented yet</p>\r\n }\r\n }\r\n } @else{\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n }\r\n\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[layout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, layout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: layout.scope?.reference\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"layout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <button\r\n [style]=\"getStyleWithoutClasses(layout?.style).style\"\r\n [class]=\"layout.option.protected ? 'disabled-button' : getStyleWithoutClasses(layout?.style).classes\"\r\n (click)=\"\r\n invokeButtonClick(\r\n layout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <ng-container\r\n *ngIf=\"!layout.caption; else textContent\">\r\n <i nz-icon [nzType]=\"layout.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ layout.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n\r\n <!-- HorizontalLayout Control -->\r\n <div *ngSwitchCase=\"'HorizontalLayout'\">\r\n <div class=\"card-horizontal-layout\"\r\n [class]=\"getStyleWithoutClasses(\r\n layout?.style\r\n ).classes\" [ngStyle]=\"getHorizontalLayoutStyle(layout)\">\r\n <ng-container\r\n *ngFor=\"let innerLayout of layout.elements\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(innerLayout.scope?.reference && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] ? itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n <!-- Media Control -->\r\n <div\r\n *ngSwitchCase=\"'media'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n\r\n <ng-container\r\n *ngTemplateOutlet=\"mediaContent; context: {\r\n layout: innerLayout,\r\n record: record\r\n }\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(innerLayout.option &&\r\n innerLayout.option.control &&\r\n !innerLayout.option.protected){\r\n @switch\r\n (innerLayout.option.control)\r\n {\r\n @case ('textInput') {\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template\r\n #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template\r\n #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported\r\n yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported\r\n yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported\r\n yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported\r\n yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported\r\n yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[innerLayout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, innerLayout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: innerLayout.scope?.reference\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else{\r\n\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template\r\n #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template\r\n #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n {{\r\n record[innerLayout.scope?.reference.split('/').pop()]\r\n | currency:\r\n innerLayout.option?.currency\r\n ?\r\n innerLayout.option.currency\r\n :\r\n 'USD'\r\n }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.dateFormat\r\n ?\r\n innerLayout.option?.dateFormat\r\n :\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'yyyy-MM-dd') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.timeFormat\r\n ?\r\n innerLayout.option?.timeFormat\r\n :itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'HH:mm') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <select\r\n id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[innerLayout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, innerLayout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: innerLayout.scope?.reference\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"innerLayout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button nz-button\r\n [matTooltip]=\"getHorizontalLayoutTooltip(innerLayout)\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.style).style\"\r\n [class]=\"innerLayout.option?.visible ? innerLayout.option.protected ? 'disabled-button' : getStyleWithoutClasses(innerLayout?.style).classes : 'hidden'\"\r\n (click)=\"\r\n invokeButtonClick(\r\n innerLayout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <span\r\n class=\"schema-btn-content\">\r\n <i\r\n *ngIf=\"showButtonIcon(innerLayout)\"\r\n nz-icon\r\n [nzType]=\"innerLayout.icon\"\r\n nzTheme=\"outline\">\r\n </i>\r\n\r\n <span\r\n *ngIf=\"showButtonCaption(innerLayout)\">\r\n {{\r\n innerLayout.caption\r\n }}\r\n </span>\r\n\r\n <span *ngIf=\"\r\n !showButtonIcon(innerLayout) &&\r\n !showButtonCaption(innerLayout)\r\n \">\r\n Button\r\n </span>\r\n </span>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div\r\n *ngIf=\"itemInfo?.footer?.elements && itemInfo?.data && itemInfo?.data.length\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.footer?.style).classes\">\r\n <span\r\n *ngFor=\"let element of itemInfo.footer?.elements; let c = index\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <h5>{{\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\">\r\n <div\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value :\r\n element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-date-picker\r\n [required]=\"element.option.required\"\r\n [name]=\"element.scope?.reference\"\r\n [nzDisabled]=\"element?.option?.disabled\"\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [nzFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format : 'yyyy-MM-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\"></nz-date-picker>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardFooter(element)\">\r\n <button nz-button\r\n [style]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else textContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ element.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </span>\r\n </div>\r\n <!-- bottom pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.data?.length && \r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n !itemInfo?.layoutSchema?.pagination?.top &&\r\n !scrollPagination\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\"\r\n [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n\r\n @if (scrollPagination) {\r\n <div #scrollPaginationSentinel class=\"scroll-pagination-sentinel\"\r\n aria-hidden=\"true\">\r\n </div>\r\n }\r\n</div>\r\n<ng-template #mediaContent let-layout=\"layout\" let-record=\"record\">\r\n <ng-container *ngIf=\"getMediaSource(layout, record) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(layout, record)\">\r\n\r\n <!-- Default: image. Keep your existing image-click behaviour. -->\r\n <img\r\n *ngSwitchDefault\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(layout)\"\r\n loading=\"lazy\"\r\n (click)=\"showImageDetails(layout.option, record)\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(layout)\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(layout)\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(layout) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(layout) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>", styles: [":host{display:block;width:100%;min-width:0}.vl-card-runtime{min-width:0;box-sizing:border-box}.card-responsive-grid{width:100%;min-width:0;box-sizing:border-box}.scroll-pagination-sentinel{width:100%;height:1px}.card-grid-item,.card-layout-item,.card-horizontal-layout,.card-field-wrapper{width:100%;min-width:0;box-sizing:border-box}.card-field-wrapper{overflow-wrap:anywhere}.card-field-wrapper p,.card-field-wrapper h3{margin:0;min-width:0}.card-layout-item img{display:block;max-width:100%;height:auto}.card-layout-item button{max-width:100%;box-sizing:border-box}.schema-btn-content{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:0}.hidden{display:none!important}.card-pagination-wrapper{width:100%;min-width:0;box-sizing:border-box;padding:0 10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i5.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i13.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i14.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i15.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "ngmodule", type: NzModalModule }, { kind: "component", type: PaginationComponent, selector: "vl-pagination", inputs: ["itemInfo", "pageSize", "currentPage", "selectedLanguageInfo", "pages"], outputs: ["pageSizeChange", "onGoToPage"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i17.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: PlaceholderPipe, name: "vlPlaceholder" }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i5.DatePipe, name: "date" }] });
4429
4665
  }
4430
4666
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CardComponent, decorators: [{
4431
4667
  type: Component,
@@ -4444,7 +4680,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
4444
4680
  InputTextModule,
4445
4681
  CustomKeyboardDirective,
4446
4682
  ], standalone: true, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the bottom pagination read itemInfo?.data.length; before the records arrive data is\r\n undefined, the template threw and the cards stayed half drawn (luv2shop start page: one\r\n card without texts)\r\n - fix: the price fields read ...?.format.currency; a property without format threw the same\r\n way (luv2shop: no prices, cards cut off) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: the date filter uses the shared picker markup (picker-field, DateTimePickerService) -->\r\n\r\n<!-- shuaib 2026-09-24\r\n - change: card field switches use renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment naming the types this container does not render. It carries four\r\n ngSwitch blocks, so the gap belongs to the container and not to one of them -->\r\n\r\n<!-- @contract gap: dateRange, quantity, credit card\r\n reality: a card renders string, number, image, date, time, dropdown, boolean and\r\n autocomplete. Legacy checkbox fields normalize to boolean. dropdown and boolean arrived on 2026-09-18; before that a card\r\n dropped them silently.\r\n failure: a field of a missing type renders empty, with no error.\r\n instead: quantity is grid-only by design; for a dateRange or a credit card use a modal.\r\n seen: this file carries four switch blocks (lines 23, 94, 267, 488) and none of them has\r\n a case for those three - the gap belongs to the container, not to one block -->\r\n\r\n<!-- Loader Spinner -->\r\n\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div class=\"vl-card-runtime\">\r\n <!-- top pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n itemInfo?.layoutSchema?.pagination?.top\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n <div *ngIf=\"itemInfo?.header?.elements?.length\" [ngStyle]=\"getHeaderStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.header?.style\r\n ).classes\">\r\n <div *ngFor=\"let element of itemInfo.header?.elements; let c = index\"\r\n [ngStyle]=\"getHeaderElementWrapperStyle(element)\"\r\n [class]=\"element.option?.innerAction && element.option?.innerAction === 'saveAll' && !displayedRecords?.length ? \r\n 'hidden' :getStyleWithoutClasses(element?.wrapperStyle).classes\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"element.option?.visible ? 'card-field-wrapper' : 'hidden'\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"element.label?.caption\"\r\n [style]=\"getStyleWithoutClasses(element.label?.style).style\"\r\n [class]=\"element.label?.style?.classes?.[0]\">\r\n {{ element.label.caption }}\r\n </span>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <vl-string [formField]=\"element\"\r\n [modalItemInfo]=\"itemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\"\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : ''\"\r\n [ngClass]=\"element?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <div class=\"picker-field\">\r\n <p-datepicker #cardDatePicker\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [dateFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].format : 'yy-mm-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [class]=\"element?.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"element?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, cardDatePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(cardDatePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, cardDatePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardHeader(element)\">\r\n <button nz-button type=\"button\"\r\n [matTooltip]=\"element?.option?.tooltip || ''\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [ngStyle]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else headerTextContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #headerTextContent>{{ element.caption\r\n }}</ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <div class=\"card-responsive-grid\" [ngStyle]=\"getCardContainerStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.layoutSchema?.style\r\n ).classes\">\r\n <div *ngFor=\"let record of displayedRecords; let k = index\"\r\n class=\"card-grid-item\">\r\n <div [ngStyle]=\"getSingleCardStyle()\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.layoutSchema?.stylePerCard).classes\">\r\n <div *ngFor=\"let layout of itemInfo?.layoutSchema?.cards || []\"\r\n class=\"card-layout-item\" [ngStyle]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? {}\r\n : getStyleWithoutClasses(layout?.style).style\r\n \" [class]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? ''\r\n : getStyleWithoutClasses(layout?.style).classes\r\n \">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(layout?.scope?.reference && itemInfo.propertySchema[getScope(layout.scope?.reference)] ? itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.type : layout?.type == 'HorizontalLayout' ? 'HorizontalLayout' : 'default')\">\r\n <!-- Media Control -->\r\n <div\r\n *ngSwitchCase=\"'media'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <ng-container\r\n *ngTemplateOutlet=\"mediaContent; context: { layout: layout, record: record }\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(layout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout.label?.style).classes\">\r\n {{ getHorizontalLayoutCaption(layout) }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(layout.option && layout.option.control &&\r\n !layout.option.protected){\r\n @switch (layout.option.control) {\r\n @case ('textInput') {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[layout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, layout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: layout.scope?.reference\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <!-- VALUE -->\r\n @if(layout.option && layout.option.control &&\r\n !layout.option.protected){\r\n @switch (layout.option.control) {\r\n @case ('textInput') {\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n }\r\n @case ('slider') {\r\n <p>Slider not Implemented yet</p>\r\n\r\n }\r\n @case ('spinner') {\r\n <p>Spinner not Implemented yet</p>\r\n }\r\n }\r\n } @else{\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n }\r\n\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[layout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, layout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: layout.scope?.reference\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"layout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <button\r\n [style]=\"getStyleWithoutClasses(layout?.style).style\"\r\n [class]=\"layout.option.protected ? 'disabled-button' : getStyleWithoutClasses(layout?.style).classes\"\r\n (click)=\"\r\n invokeButtonClick(\r\n layout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <ng-container\r\n *ngIf=\"!layout.caption; else textContent\">\r\n <i nz-icon [nzType]=\"layout.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ layout.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n\r\n <!-- HorizontalLayout Control -->\r\n <div *ngSwitchCase=\"'HorizontalLayout'\">\r\n <div class=\"card-horizontal-layout\"\r\n [class]=\"getStyleWithoutClasses(\r\n layout?.style\r\n ).classes\" [ngStyle]=\"getHorizontalLayoutStyle(layout)\">\r\n <ng-container\r\n *ngFor=\"let innerLayout of layout.elements\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(innerLayout.scope?.reference && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] ? itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n <!-- Media Control -->\r\n <div\r\n *ngSwitchCase=\"'media'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n\r\n <ng-container\r\n *ngTemplateOutlet=\"mediaContent; context: {\r\n layout: innerLayout,\r\n record: record\r\n }\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(innerLayout.option &&\r\n innerLayout.option.control &&\r\n !innerLayout.option.protected){\r\n @switch\r\n (innerLayout.option.control)\r\n {\r\n @case ('textInput') {\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template\r\n #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template\r\n #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported\r\n yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported\r\n yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported\r\n yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported\r\n yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported\r\n yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[innerLayout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, innerLayout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: innerLayout.scope?.reference\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else{\r\n\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template\r\n #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template\r\n #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n {{\r\n record[innerLayout.scope?.reference.split('/').pop()]\r\n | currency:\r\n innerLayout.option?.currency\r\n ?\r\n innerLayout.option.currency\r\n :\r\n 'USD'\r\n }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.dateFormat\r\n ?\r\n innerLayout.option?.dateFormat\r\n :\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'yyyy-MM-dd') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.timeFormat\r\n ?\r\n innerLayout.option?.timeFormat\r\n :itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'HH:mm') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <select\r\n id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[innerLayout.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, innerLayout)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema | vlPlaceholder: innerLayout.scope?.reference\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"innerLayout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button nz-button\r\n [matTooltip]=\"getHorizontalLayoutTooltip(innerLayout)\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.style).style\"\r\n [class]=\"innerLayout.option?.visible ? innerLayout.option.protected ? 'disabled-button' : getStyleWithoutClasses(innerLayout?.style).classes : 'hidden'\"\r\n (click)=\"\r\n invokeButtonClick(\r\n innerLayout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <span\r\n class=\"schema-btn-content\">\r\n <i\r\n *ngIf=\"showButtonIcon(innerLayout)\"\r\n nz-icon\r\n [nzType]=\"innerLayout.icon\"\r\n nzTheme=\"outline\">\r\n </i>\r\n\r\n <span\r\n *ngIf=\"showButtonCaption(innerLayout)\">\r\n {{\r\n innerLayout.caption\r\n }}\r\n </span>\r\n\r\n <span *ngIf=\"\r\n !showButtonIcon(innerLayout) &&\r\n !showButtonCaption(innerLayout)\r\n \">\r\n Button\r\n </span>\r\n </span>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div\r\n *ngIf=\"itemInfo?.footer?.elements && itemInfo?.data && itemInfo?.data.length\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.footer?.style).classes\">\r\n <span\r\n *ngFor=\"let element of itemInfo.footer?.elements; let c = index\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <h5>{{\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\">\r\n <div\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value :\r\n element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-date-picker\r\n [required]=\"element.option.required\"\r\n [name]=\"element.scope?.reference\"\r\n [nzDisabled]=\"element?.option?.disabled\"\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [nzFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format : 'yyyy-MM-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\"></nz-date-picker>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardFooter(element)\">\r\n <button nz-button\r\n [style]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else textContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ element.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </span>\r\n </div>\r\n <!-- bottom pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.data?.length && \r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n !itemInfo?.layoutSchema?.pagination?.top &&\r\n !scrollPagination\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\"\r\n [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n\r\n @if (scrollPagination) {\r\n <div #scrollPaginationSentinel class=\"scroll-pagination-sentinel\"\r\n aria-hidden=\"true\">\r\n </div>\r\n }\r\n</div>\r\n<ng-template #mediaContent let-layout=\"layout\" let-record=\"record\">\r\n <ng-container *ngIf=\"getMediaSource(layout, record) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(layout, record)\">\r\n\r\n <!-- Default: image. Keep your existing image-click behaviour. -->\r\n <img\r\n *ngSwitchDefault\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(layout)\"\r\n loading=\"lazy\"\r\n (click)=\"showImageDetails(layout.option, record)\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(layout)\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(layout)\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(layout) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(layout) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>", styles: [":host{display:block;width:100%;min-width:0}.vl-card-runtime{min-width:0;box-sizing:border-box}.card-responsive-grid{width:100%;min-width:0;box-sizing:border-box}.scroll-pagination-sentinel{width:100%;height:1px}.card-grid-item,.card-layout-item,.card-horizontal-layout,.card-field-wrapper{width:100%;min-width:0;box-sizing:border-box}.card-field-wrapper{overflow-wrap:anywhere}.card-field-wrapper p,.card-field-wrapper h3{margin:0;min-width:0}.card-layout-item img{display:block;max-width:100%;height:auto}.card-layout-item button{max-width:100%;box-sizing:border-box}.schema-btn-content{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:0}.hidden{display:none!important}.card-pagination-wrapper{width:100%;min-width:0;box-sizing:border-box;padding:0 10px}\n"] }]
4447
- }], ctorParameters: () => [{ type: UtilityService }, { type: i1.Platform }, { type: i1$2.HttpClient }, { type: ConfigService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i5.NzModalService }, { type: NotificationService }, { type: i7.OAuthService }, { type: RequestService }], propDecorators: { itemInfo: [{
4683
+ }], ctorParameters: () => [{ type: UtilityService }, { type: i1.Platform }, { type: i1$1.HttpClient }, { type: ConfigService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i5$1.NzModalService }, { type: NotificationService }, { type: i7.OAuthService }, { type: RequestService }], propDecorators: { itemInfo: [{
4448
4684
  type: Input
4449
4685
  }], currentPage: [{
4450
4686
  type: Input
@@ -4537,6 +4773,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
4537
4773
  type: Input
4538
4774
  }] } });
4539
4775
 
4776
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-15
4777
+ // - new (#145): vlMedia - a media field's value made playable for <video>/<audio>: a file name is
4778
+ // read from Media/ of the project (signed CouchDB link for an attachment), a URL stays as it is
4779
+ /** [src]="source | vlMedia | async" - the same Promise for the same source, so no request loop. */
4780
+ class MediaSourcePipe {
4781
+ couchdb;
4782
+ /** Kept one hour: a signed link lasts eight, a page lives shorter. */
4783
+ static known = new Map();
4784
+ constructor(couchdb) {
4785
+ this.couchdb = couchdb;
4786
+ }
4787
+ transform(source) {
4788
+ const value = (source || '').trim();
4789
+ const known = MediaSourcePipe.known.get(value);
4790
+ if (known && Date.now() - known.at < 3600_000)
4791
+ return known.pending;
4792
+ const pending = this.couchdb.resolveMediaSource(value).catch((error) => {
4793
+ console.warn(`Media "${value}" could not be resolved`, error);
4794
+ MediaSourcePipe.known.delete(value);
4795
+ return '';
4796
+ });
4797
+ MediaSourcePipe.known.set(value, { pending, at: Date.now() });
4798
+ return pending;
4799
+ }
4800
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MediaSourcePipe, deps: [{ token: Couchdb }], target: i0.ɵɵFactoryTarget.Pipe });
4801
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.2", ngImport: i0, type: MediaSourcePipe, isStandalone: true, name: "vlMedia" });
4802
+ }
4803
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MediaSourcePipe, decorators: [{
4804
+ type: Pipe,
4805
+ args: [{
4806
+ name: 'vlMedia',
4807
+ standalone: true,
4808
+ }]
4809
+ }], ctorParameters: () => [{ type: Couchdb }] });
4810
+
4811
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-16
4812
+ // - fix: setErroMessage without a text for the error no longer throws (error.replace of undefined)
4813
+ // - new: an autocomplete says "loading" while its backend list is on the way and "no results" only
4814
+ // after, in the project's language (autocompleteLoading / autocompleteNoResults)
4815
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-15
4816
+ // - new (#145): video and audio play through vlMedia - a file name in the field becomes a signed
4817
+ // CouchDB link (attachment, streamed with Range)
4540
4818
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
4541
4819
  // - new: fields with a backend source (option.autocomplete) load their list through
4542
4820
  // RemoteListService - autocomplete suggestions, dropdown/combobox items; the autocompletes of this
@@ -4572,8 +4850,15 @@ class FormComponent {
4572
4850
  };
4573
4851
  nodes.forEach(visit);
4574
4852
  await Promise.all([...sources.entries()].map(async ([reference, node]) => {
4575
- const list = await this.remoteLists.loadShared(node.option.autocomplete);
4853
+ this.remoteLoading[reference] = true;
4854
+ const list = await this.remoteLists.loadShared(node.option.autocomplete).finally(() => {
4855
+ this.remoteLoading[reference] = false;
4856
+ });
4576
4857
  this.remoteData[reference] = list;
4858
+ // the list opened while it was still loading: show what arrived for what was typed
4859
+ if (this.lastRemoteQuery?.reference === reference) {
4860
+ this.completeRemote({ query: this.lastRemoteQuery.query }, node);
4861
+ }
4577
4862
  const scope = reference.substring(reference.indexOf('#/') + 2, reference.indexOf('/', reference.indexOf('#/') + 2));
4578
4863
  const property = propertySchema?.[scope]?.[reference.split('/').pop() ?? ''];
4579
4864
  if (property && node.option?.control && node.option.control !== 'autocomplete' && list.length) {
@@ -4581,9 +4866,24 @@ class FormComponent {
4581
4866
  }
4582
4867
  }));
4583
4868
  }
4869
+ /** Backend lists still on the way, by field reference. */
4870
+ remoteLoading = {};
4871
+ lastRemoteQuery = null;
4872
+ /**
4873
+ * What an autocomplete shows when it has no entries: "loading" while its list is on the way,
4874
+ * "no results" after - in the project's language (autocompleteLoading, autocompleteNoResults),
4875
+ * English when the project has no text for it.
4876
+ */
4877
+ autocompleteEmptyText(node) {
4878
+ const t = this.selectedLanguageInfo?.selectedLanguage || {};
4879
+ return this.remoteLoading[node?.scope?.reference ?? '']
4880
+ ? (t['autocompleteLoading'] || 'Loading…')
4881
+ : (t['autocompleteNoResults'] || 'No results');
4882
+ }
4584
4883
  /** (completeMethod) of an autocomplete with a backend list. */
4585
4884
  completeRemote(event, node) {
4586
4885
  const reference = node?.scope?.reference ?? '';
4886
+ this.lastRemoteQuery = { reference, query: event?.query || '' };
4587
4887
  this.remoteSuggestions[reference] = this.remoteLists.filter(this.remoteData[reference], reference.split('/').pop(), event?.query);
4588
4888
  }
4589
4889
  /** Date and time picker behaviour shared by all containers. */
@@ -4797,7 +5097,9 @@ class FormComponent {
4797
5097
  if (error && error.toString().includes('{{requiredLength}}')) {
4798
5098
  error = error.replace(/{{\s*requiredLength\s*}}/g, formField?.minlength?.toString() || '');
4799
5099
  }
4800
- return error.replace(/{{\s*field\s*}}/g, fieldName);
5100
+ // no text for this error in the project (errorMessages not loaded): nothing to show, no crash -
5101
+ // the exception came on every change detection of the dialog and stopped it updating
5102
+ return `${error ?? ''}`.replace(/{{\s*field\s*}}/g, fieldName ?? '');
4801
5103
  }
4802
5104
  setValidations() {
4803
5105
  if (!this.form?.layoutSchema?.formSections?.length) {
@@ -4903,17 +5205,17 @@ class FormComponent {
4903
5205
  return 'image';
4904
5206
  }
4905
5207
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: FormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4906
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: FormComponent, isStandalone: true, selector: "vl-form", inputs: { errorMessages: "errorMessages", creditCardError: "creditCardError", form: "form", selectedLanguageInfo: "selectedLanguageInfo" }, outputs: { formButtonClick: "formButtonClick", checkBoxChangeEvent: "checkBoxChangeEvent" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time fields use the shared picker markup (picker-field, DateTimePickerService)\r\n instead of PrimeNG's own icon with empty handlers -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the form field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - change: the credit card type is spelled creditCard. The rename of 2026-09-15 reached\r\n model.component.html and the whole Designer but not this file, so one data object rendered\r\n in a modal and stayed empty here\r\n - new: a @contract comment on the type switch and on the credit card case, so the rule sits\r\n next to the code that implements it -->\r\n<div class=\"form-renderer\"\r\n [ngClass]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.style\">\r\n <form *ngIf=\"form?.layoutSchema?.formSections?.length > 0\">\r\n <div\r\n *ngFor=\"let formSection of form?.layoutSchema?.formSections; let i = index\"\r\n class=\"section-card\">\r\n <div *ngIf=\"formSection?.label?.caption\" class=\"section-header\">\r\n <div class=\"section-title\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formSection?.label?.style)?.style\">\r\n {{ formSection.label.caption }}\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"formSection?.formFields?.length > 0\" class=\"section-body\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.style)?.classes\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\" class=\"field-row\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\">\r\n <!-- @contract decision: this switch decides whether a field appears at all\r\n reality: renders string, number, image, date, time, dropdown, boolean,\r\n autocomplete, dateRange and the credit card type. Everything else\r\n falls into the default branch and the field renders empty - no error,\r\n nothing in the console either. Existing checkbox fields are normalized\r\n to boolean before this switch; new fields should use boolean.\r\n seen: measured 2026-09-19 with viewdefinition-contract/derive-capabilities.sh\r\n against this file; date, time, dropdown, boolean, autocomplete and\r\n dateRange arrived on 2026-09-18 -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n form?.propertySchema?.[getScope(formField.scope?.reference)]\r\n ? form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- Media control -->\r\n\r\n <div *ngSwitchCase=\"'media'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <ng-container *ngTemplateOutlet=\"formMediaContent; context: { field: formField }\"></ng-container>\r\n </div>\r\n\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: it was carditCard here until 2026-09-19. The rename of 2026-09-15\r\n reached model.component.html and the whole Designer, but not this\r\n file and not utility.service.ts, so the same data object rendered in\r\n one container and stayed empty in the other. All four places agree now.\r\n failure: a data object still declaring carditCard renders empty everywhere -\r\n that spelling is read nowhere any more.\r\n instead: declare the type as creditCard. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <div id=\"card-element\"></div>\r\n <div id=\"card-errors\"\r\n [class]=\"creditCardError?.textContent !== '' ? 'text-red-400 m-0' : ''\"></div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <span *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between\">\r\n <span\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </span>\r\n\r\n <span>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n : formField?.label?.caption\r\n ? formField.label.caption\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <div [ngStyle]=\"\r\n formField?.option?.password\r\n ? {\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center'\r\n }\r\n : {}\r\n \">\r\n <input [type]=\"\r\n formField?.option?.password\r\n ? !showPassword\r\n ? 'password'\r\n : 'text'\r\n : 'text'\r\n \" #inputField=\"ngModel\" [required]=\"formField?.required\"\r\n [attr.inputmode]=\"\r\n formField?.option?.email\r\n ? 'email'\r\n : formField?.option?.password\r\n ? 'numeric'\r\n : 'text'\r\n \" [pattern]=\"formField?.email ? customEmailPattern : ''\"\r\n [email]=\"formField?.email\"\r\n [minlength]=\"formField?.minlength\"\r\n [maxlength]=\"formField?.maxlength\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n\r\n <i *ngIf=\"formField?.option?.password\" class=\"fa\"\r\n [ngClass]=\"showPassword ? 'fa-eye' : 'fa-eye-slash'\"\r\n (click)=\"togglePasswordVisibility()\"\r\n style=\"position: absolute; right: 10px; cursor: pointer\"></i>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"inputField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, inputField) }}\r\n </p>\r\n </div>\r\n </span>\r\n </span>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n <h5>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n | currency\r\n : formField?.option?.currency\r\n : true\r\n : formField?.option?.decimalPoint\r\n }}\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <input type=\"number\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- checkbox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <label class=\"form-check-label\">\r\n <input type=\"checkbox\" class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\"\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label class=\"form-check-label\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n <input type=\"checkbox\" class=\"form-check-input\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n </label>\r\n </div>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\" [dateFormat]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\" [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"formField?.option?.disabled\" [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete [suggestions]=\"remoteSuggestions[formField.scope?.reference] ?? []\" (completeMethod)=\"completeRemote($event, formField)\" #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\" [dropdown]=\"true\"\r\n [optionLabel]=\"formField.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[formField?.option?.optionValue]\">\r\n {{ item[formField?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <nz-select nzShowSearch nzAllowClear nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField?.required\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <nz-option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [nzLabel]=\"item[formField?.option?.optionLabel]\"\r\n [nzValue]=\"item[formField?.option?.optionValue]\"></nz-option>\r\n </nz-select>\r\n </div>\r\n\r\n <div *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"dropdownField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, dropdownField)\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button/default control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.style)?.style\"\r\n (click)=\"invokeFormBtnClick(formField?.option, form.propertySchema)\">\r\n {{ formField?.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n\r\n<ng-template #formMediaContent let-field=\"field\">\r\n <ng-container *ngIf=\"getMediaSource(field) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(field)\">\r\n <img *ngSwitchDefault [src]=\"source\" [alt]=\"getMediaLabel(field)\" loading=\"lazy\" />\r\n <video *ngSwitchCase=\"'video'\" [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(field)\" controls playsinline preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n <audio *ngSwitchCase=\"'audio'\" [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(field)\" controls preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n <a *ngSwitchCase=\"'pdf'\" [href]=\"source\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n <a *ngSwitchCase=\"'file'\" [href]=\"source\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>\r\n\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.form-renderer{width:100%;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:12px}.section-card{border:1px solid #e5e7eb;border-radius:var(--radius-sm, 8px);padding:12px;margin-bottom:12px;background:#fff;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;font-weight:600}.section-title{font-size:14px;color:#0f172a}.section-body{position:relative;width:100%;min-height:78px;box-sizing:border-box}.field-row{position:relative;min-width:0;width:100%;box-sizing:border-box}.field-row input,.field-row select,.field-row nz-select,.field-row textarea,.field-row button{max-width:100%;box-sizing:border-box}@media(max-width:768px){.section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}.field-row{grid-column:span 1!important}}@media(min-width:769px)and (max-width:1024px){.section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}.field-row{grid-column:span 6!important}}:host-context(.mobile-frame) .section-body,:host-context(.device-mobile) .section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}:host-context(.mobile-frame) .field-row,:host-context(.device-mobile) .field-row{grid-column:span 1!important}:host-context(.tablet-frame) .section-body,:host-context(.device-tablet) .section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}:host-context(.tablet-frame) .field-row,:host-context(.device-tablet) .field-row{grid-column:span 6!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzVariant", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzPrefix", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus", "nzOnClear"], exportAs: ["nzSelect"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: Label, selector: "vl-label", inputs: ["formField", "modalItemInfo"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: PlaceholderPipe, name: "vlPlaceholder" }, { kind: "pipe", type: i1$1.CurrencyPipe, name: "currency" }] });
5208
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: FormComponent, isStandalone: true, selector: "vl-form", inputs: { errorMessages: "errorMessages", creditCardError: "creditCardError", form: "form", selectedLanguageInfo: "selectedLanguageInfo" }, outputs: { formButtonClick: "formButtonClick", checkBoxChangeEvent: "checkBoxChangeEvent" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time fields use the shared picker markup (picker-field, DateTimePickerService)\r\n instead of PrimeNG's own icon with empty handlers -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the form field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - change: the credit card type is spelled creditCard. The rename of 2026-09-15 reached\r\n model.component.html and the whole Designer but not this file, so one data object rendered\r\n in a modal and stayed empty here\r\n - new: a @contract comment on the type switch and on the credit card case, so the rule sits\r\n next to the code that implements it -->\r\n<div class=\"form-renderer\"\r\n [ngClass]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.style\">\r\n <form *ngIf=\"form?.layoutSchema?.formSections?.length > 0\">\r\n <div\r\n *ngFor=\"let formSection of form?.layoutSchema?.formSections; let i = index\"\r\n class=\"section-card\">\r\n <div *ngIf=\"formSection?.label?.caption\" class=\"section-header\">\r\n <div class=\"section-title\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formSection?.label?.style)?.style\">\r\n {{ formSection.label.caption }}\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"formSection?.formFields?.length > 0\" class=\"section-body\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.style)?.classes\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\" class=\"field-row\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\">\r\n <!-- @contract decision: this switch decides whether a field appears at all\r\n reality: renders string, number, image, date, time, dropdown, boolean,\r\n autocomplete, dateRange and the credit card type. Everything else\r\n falls into the default branch and the field renders empty - no error,\r\n nothing in the console either. Existing checkbox fields are normalized\r\n to boolean before this switch; new fields should use boolean.\r\n seen: measured 2026-09-19 with viewdefinition-contract/derive-capabilities.sh\r\n against this file; date, time, dropdown, boolean, autocomplete and\r\n dateRange arrived on 2026-09-18 -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n form?.propertySchema?.[getScope(formField.scope?.reference)]\r\n ? form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- Media control -->\r\n\r\n <div *ngSwitchCase=\"'media'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <ng-container *ngTemplateOutlet=\"formMediaContent; context: { field: formField }\"></ng-container>\r\n </div>\r\n\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: it was carditCard here until 2026-09-19. The rename of 2026-09-15\r\n reached model.component.html and the whole Designer, but not this\r\n file and not utility.service.ts, so the same data object rendered in\r\n one container and stayed empty in the other. All four places agree now.\r\n failure: a data object still declaring carditCard renders empty everywhere -\r\n that spelling is read nowhere any more.\r\n instead: declare the type as creditCard. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <div id=\"card-element\"></div>\r\n <div id=\"card-errors\"\r\n [class]=\"creditCardError?.textContent !== '' ? 'text-red-400 m-0' : ''\"></div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <span *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between\">\r\n <span\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </span>\r\n\r\n <span>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n : formField?.label?.caption\r\n ? formField.label.caption\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <div [ngStyle]=\"\r\n formField?.option?.password\r\n ? {\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center'\r\n }\r\n : {}\r\n \">\r\n <input [type]=\"\r\n formField?.option?.password\r\n ? !showPassword\r\n ? 'password'\r\n : 'text'\r\n : 'text'\r\n \" #inputField=\"ngModel\" [required]=\"formField?.required\"\r\n [attr.inputmode]=\"\r\n formField?.option?.email\r\n ? 'email'\r\n : formField?.option?.password\r\n ? 'numeric'\r\n : 'text'\r\n \" [pattern]=\"formField?.email ? customEmailPattern : ''\"\r\n [email]=\"formField?.email\"\r\n [minlength]=\"formField?.minlength\"\r\n [maxlength]=\"formField?.maxlength\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n\r\n <i *ngIf=\"formField?.option?.password\" class=\"fa\"\r\n [ngClass]=\"showPassword ? 'fa-eye' : 'fa-eye-slash'\"\r\n (click)=\"togglePasswordVisibility()\"\r\n style=\"position: absolute; right: 10px; cursor: pointer\"></i>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"inputField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, inputField) }}\r\n </p>\r\n </div>\r\n </span>\r\n </span>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n <h5>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n | currency\r\n : formField?.option?.currency\r\n : true\r\n : formField?.option?.decimalPoint\r\n }}\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <input type=\"number\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- checkbox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <label class=\"form-check-label\">\r\n <input type=\"checkbox\" class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\"\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label class=\"form-check-label\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n <input type=\"checkbox\" class=\"form-check-input\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n </label>\r\n </div>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\" [dateFormat]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\" [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"formField?.option?.disabled\" [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete [suggestions]=\"remoteSuggestions[formField.scope?.reference] ?? []\" (completeMethod)=\"completeRemote($event, formField)\"\r\n [emptyMessage]=\"autocompleteEmptyText(formField)\" [showEmptyMessage]=\"true\" #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\" [dropdown]=\"true\"\r\n [optionLabel]=\"formField.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[formField?.option?.optionValue]\">\r\n {{ item[formField?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <nz-select nzShowSearch nzAllowClear nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField?.required\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <nz-option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [nzLabel]=\"item[formField?.option?.optionLabel]\"\r\n [nzValue]=\"item[formField?.option?.optionValue]\"></nz-option>\r\n </nz-select>\r\n </div>\r\n\r\n <div *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"dropdownField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, dropdownField)\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button/default control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.style)?.style\"\r\n (click)=\"invokeFormBtnClick(formField?.option, form.propertySchema)\">\r\n {{ formField?.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n\r\n<ng-template #formMediaContent let-field=\"field\">\r\n <ng-container *ngIf=\"getMediaSource(field) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(field)\">\r\n <img *ngSwitchDefault [src]=\"source\" [alt]=\"getMediaLabel(field)\" loading=\"lazy\" />\r\n <video *ngSwitchCase=\"'video'\" [src]=\"(source | vlMedia | async) || null\"\r\n [attr.aria-label]=\"getMediaLabel(field)\" controls playsinline preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n <audio *ngSwitchCase=\"'audio'\" [src]=\"(source | vlMedia | async) || null\"\r\n [attr.aria-label]=\"getMediaLabel(field)\" controls preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n <a *ngSwitchCase=\"'pdf'\" [href]=\"source\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n <a *ngSwitchCase=\"'file'\" [href]=\"source\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>\r\n\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.form-renderer{width:100%;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:12px}.section-card{border:1px solid #e5e7eb;border-radius:var(--radius-sm, 8px);padding:12px;margin-bottom:12px;background:#fff;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;font-weight:600}.section-title{font-size:14px;color:#0f172a}.section-body{position:relative;width:100%;min-height:78px;box-sizing:border-box}.field-row{position:relative;min-width:0;width:100%;box-sizing:border-box}.field-row input,.field-row select,.field-row nz-select,.field-row textarea,.field-row button{max-width:100%;box-sizing:border-box}@media(max-width:768px){.section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}.field-row{grid-column:span 1!important}}@media(min-width:769px)and (max-width:1024px){.section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}.field-row{grid-column:span 6!important}}:host-context(.mobile-frame) .section-body,:host-context(.device-mobile) .section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}:host-context(.mobile-frame) .field-row,:host-context(.device-mobile) .field-row{grid-column:span 1!important}:host-context(.tablet-frame) .section-body,:host-context(.device-tablet) .section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}:host-context(.tablet-frame) .field-row,:host-context(.device-tablet) .field-row{grid-column:span 6!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i5.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzVariant", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzPrefix", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus", "nzOnClear"], exportAs: ["nzSelect"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: Label, selector: "vl-label", inputs: ["formField", "modalItemInfo"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: PlaceholderPipe, name: "vlPlaceholder" }, { kind: "pipe", type: MediaSourcePipe, name: "vlMedia" }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }] });
4907
5209
  }
4908
5210
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: FormComponent, decorators: [{
4909
5211
  type: Component,
4910
- args: [{ standalone: true, imports: [PlaceholderPipe, CommonModule, FormsModule, NzSelectModule, String$1, Label,
5212
+ args: [{ standalone: true, imports: [PlaceholderPipe, MediaSourcePipe, CommonModule, FormsModule, NzSelectModule, String$1, Label,
4911
5213
  NzDatePickerModule,
4912
5214
  DatePickerModule,
4913
5215
  AutoCompleteModule,
4914
5216
  InputTextModule,
4915
5217
  CustomKeyboardDirective,
4916
- ], selector: 'vl-form', template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time fields use the shared picker markup (picker-field, DateTimePickerService)\r\n instead of PrimeNG's own icon with empty handlers -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the form field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - change: the credit card type is spelled creditCard. The rename of 2026-09-15 reached\r\n model.component.html and the whole Designer but not this file, so one data object rendered\r\n in a modal and stayed empty here\r\n - new: a @contract comment on the type switch and on the credit card case, so the rule sits\r\n next to the code that implements it -->\r\n<div class=\"form-renderer\"\r\n [ngClass]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.style\">\r\n <form *ngIf=\"form?.layoutSchema?.formSections?.length > 0\">\r\n <div\r\n *ngFor=\"let formSection of form?.layoutSchema?.formSections; let i = index\"\r\n class=\"section-card\">\r\n <div *ngIf=\"formSection?.label?.caption\" class=\"section-header\">\r\n <div class=\"section-title\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formSection?.label?.style)?.style\">\r\n {{ formSection.label.caption }}\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"formSection?.formFields?.length > 0\" class=\"section-body\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.style)?.classes\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\" class=\"field-row\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\">\r\n <!-- @contract decision: this switch decides whether a field appears at all\r\n reality: renders string, number, image, date, time, dropdown, boolean,\r\n autocomplete, dateRange and the credit card type. Everything else\r\n falls into the default branch and the field renders empty - no error,\r\n nothing in the console either. Existing checkbox fields are normalized\r\n to boolean before this switch; new fields should use boolean.\r\n seen: measured 2026-09-19 with viewdefinition-contract/derive-capabilities.sh\r\n against this file; date, time, dropdown, boolean, autocomplete and\r\n dateRange arrived on 2026-09-18 -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n form?.propertySchema?.[getScope(formField.scope?.reference)]\r\n ? form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- Media control -->\r\n\r\n <div *ngSwitchCase=\"'media'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <ng-container *ngTemplateOutlet=\"formMediaContent; context: { field: formField }\"></ng-container>\r\n </div>\r\n\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: it was carditCard here until 2026-09-19. The rename of 2026-09-15\r\n reached model.component.html and the whole Designer, but not this\r\n file and not utility.service.ts, so the same data object rendered in\r\n one container and stayed empty in the other. All four places agree now.\r\n failure: a data object still declaring carditCard renders empty everywhere -\r\n that spelling is read nowhere any more.\r\n instead: declare the type as creditCard. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <div id=\"card-element\"></div>\r\n <div id=\"card-errors\"\r\n [class]=\"creditCardError?.textContent !== '' ? 'text-red-400 m-0' : ''\"></div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <span *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between\">\r\n <span\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </span>\r\n\r\n <span>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n : formField?.label?.caption\r\n ? formField.label.caption\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <div [ngStyle]=\"\r\n formField?.option?.password\r\n ? {\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center'\r\n }\r\n : {}\r\n \">\r\n <input [type]=\"\r\n formField?.option?.password\r\n ? !showPassword\r\n ? 'password'\r\n : 'text'\r\n : 'text'\r\n \" #inputField=\"ngModel\" [required]=\"formField?.required\"\r\n [attr.inputmode]=\"\r\n formField?.option?.email\r\n ? 'email'\r\n : formField?.option?.password\r\n ? 'numeric'\r\n : 'text'\r\n \" [pattern]=\"formField?.email ? customEmailPattern : ''\"\r\n [email]=\"formField?.email\"\r\n [minlength]=\"formField?.minlength\"\r\n [maxlength]=\"formField?.maxlength\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n\r\n <i *ngIf=\"formField?.option?.password\" class=\"fa\"\r\n [ngClass]=\"showPassword ? 'fa-eye' : 'fa-eye-slash'\"\r\n (click)=\"togglePasswordVisibility()\"\r\n style=\"position: absolute; right: 10px; cursor: pointer\"></i>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"inputField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, inputField) }}\r\n </p>\r\n </div>\r\n </span>\r\n </span>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n <h5>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n | currency\r\n : formField?.option?.currency\r\n : true\r\n : formField?.option?.decimalPoint\r\n }}\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <input type=\"number\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- checkbox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <label class=\"form-check-label\">\r\n <input type=\"checkbox\" class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\"\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label class=\"form-check-label\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n <input type=\"checkbox\" class=\"form-check-input\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n </label>\r\n </div>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\" [dateFormat]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\" [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"formField?.option?.disabled\" [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete [suggestions]=\"remoteSuggestions[formField.scope?.reference] ?? []\" (completeMethod)=\"completeRemote($event, formField)\" #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\" [dropdown]=\"true\"\r\n [optionLabel]=\"formField.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[formField?.option?.optionValue]\">\r\n {{ item[formField?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <nz-select nzShowSearch nzAllowClear nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField?.required\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <nz-option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [nzLabel]=\"item[formField?.option?.optionLabel]\"\r\n [nzValue]=\"item[formField?.option?.optionValue]\"></nz-option>\r\n </nz-select>\r\n </div>\r\n\r\n <div *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"dropdownField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, dropdownField)\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button/default control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.style)?.style\"\r\n (click)=\"invokeFormBtnClick(formField?.option, form.propertySchema)\">\r\n {{ formField?.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n\r\n<ng-template #formMediaContent let-field=\"field\">\r\n <ng-container *ngIf=\"getMediaSource(field) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(field)\">\r\n <img *ngSwitchDefault [src]=\"source\" [alt]=\"getMediaLabel(field)\" loading=\"lazy\" />\r\n <video *ngSwitchCase=\"'video'\" [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(field)\" controls playsinline preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n <audio *ngSwitchCase=\"'audio'\" [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(field)\" controls preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n <a *ngSwitchCase=\"'pdf'\" [href]=\"source\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n <a *ngSwitchCase=\"'file'\" [href]=\"source\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>\r\n\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.form-renderer{width:100%;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:12px}.section-card{border:1px solid #e5e7eb;border-radius:var(--radius-sm, 8px);padding:12px;margin-bottom:12px;background:#fff;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;font-weight:600}.section-title{font-size:14px;color:#0f172a}.section-body{position:relative;width:100%;min-height:78px;box-sizing:border-box}.field-row{position:relative;min-width:0;width:100%;box-sizing:border-box}.field-row input,.field-row select,.field-row nz-select,.field-row textarea,.field-row button{max-width:100%;box-sizing:border-box}@media(max-width:768px){.section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}.field-row{grid-column:span 1!important}}@media(min-width:769px)and (max-width:1024px){.section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}.field-row{grid-column:span 6!important}}:host-context(.mobile-frame) .section-body,:host-context(.device-mobile) .section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}:host-context(.mobile-frame) .field-row,:host-context(.device-mobile) .field-row{grid-column:span 1!important}:host-context(.tablet-frame) .section-body,:host-context(.device-tablet) .section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}:host-context(.tablet-frame) .field-row,:host-context(.device-tablet) .field-row{grid-column:span 6!important}\n"] }]
5218
+ ], selector: 'vl-form', template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time fields use the shared picker markup (picker-field, DateTimePickerService)\r\n instead of PrimeNG's own icon with empty handlers -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the form field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - change: the credit card type is spelled creditCard. The rename of 2026-09-15 reached\r\n model.component.html and the whole Designer but not this file, so one data object rendered\r\n in a modal and stayed empty here\r\n - new: a @contract comment on the type switch and on the credit card case, so the rule sits\r\n next to the code that implements it -->\r\n<div class=\"form-renderer\"\r\n [ngClass]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.style\">\r\n <form *ngIf=\"form?.layoutSchema?.formSections?.length > 0\">\r\n <div\r\n *ngFor=\"let formSection of form?.layoutSchema?.formSections; let i = index\"\r\n class=\"section-card\">\r\n <div *ngIf=\"formSection?.label?.caption\" class=\"section-header\">\r\n <div class=\"section-title\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formSection?.label?.style)?.style\">\r\n {{ formSection.label.caption }}\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"formSection?.formFields?.length > 0\" class=\"section-body\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.style)?.classes\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\" class=\"field-row\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\">\r\n <!-- @contract decision: this switch decides whether a field appears at all\r\n reality: renders string, number, image, date, time, dropdown, boolean,\r\n autocomplete, dateRange and the credit card type. Everything else\r\n falls into the default branch and the field renders empty - no error,\r\n nothing in the console either. Existing checkbox fields are normalized\r\n to boolean before this switch; new fields should use boolean.\r\n seen: measured 2026-09-19 with viewdefinition-contract/derive-capabilities.sh\r\n against this file; date, time, dropdown, boolean, autocomplete and\r\n dateRange arrived on 2026-09-18 -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n form?.propertySchema?.[getScope(formField.scope?.reference)]\r\n ? form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- Media control -->\r\n\r\n <div *ngSwitchCase=\"'media'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <ng-container *ngTemplateOutlet=\"formMediaContent; context: { field: formField }\"></ng-container>\r\n </div>\r\n\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: it was carditCard here until 2026-09-19. The rename of 2026-09-15\r\n reached model.component.html and the whole Designer, but not this\r\n file and not utility.service.ts, so the same data object rendered in\r\n one container and stayed empty in the other. All four places agree now.\r\n failure: a data object still declaring carditCard renders empty everywhere -\r\n that spelling is read nowhere any more.\r\n instead: declare the type as creditCard. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <div id=\"card-element\"></div>\r\n <div id=\"card-errors\"\r\n [class]=\"creditCardError?.textContent !== '' ? 'text-red-400 m-0' : ''\"></div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <span *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between\">\r\n <span\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </span>\r\n\r\n <span>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n : formField?.label?.caption\r\n ? formField.label.caption\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <div [ngStyle]=\"\r\n formField?.option?.password\r\n ? {\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center'\r\n }\r\n : {}\r\n \">\r\n <input [type]=\"\r\n formField?.option?.password\r\n ? !showPassword\r\n ? 'password'\r\n : 'text'\r\n : 'text'\r\n \" #inputField=\"ngModel\" [required]=\"formField?.required\"\r\n [attr.inputmode]=\"\r\n formField?.option?.email\r\n ? 'email'\r\n : formField?.option?.password\r\n ? 'numeric'\r\n : 'text'\r\n \" [pattern]=\"formField?.email ? customEmailPattern : ''\"\r\n [email]=\"formField?.email\"\r\n [minlength]=\"formField?.minlength\"\r\n [maxlength]=\"formField?.maxlength\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n\r\n <i *ngIf=\"formField?.option?.password\" class=\"fa\"\r\n [ngClass]=\"showPassword ? 'fa-eye' : 'fa-eye-slash'\"\r\n (click)=\"togglePasswordVisibility()\"\r\n style=\"position: absolute; right: 10px; cursor: pointer\"></i>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"inputField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, inputField) }}\r\n </p>\r\n </div>\r\n </span>\r\n </span>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n <h5>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n | currency\r\n : formField?.option?.currency\r\n : true\r\n : formField?.option?.decimalPoint\r\n }}\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <input type=\"number\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- checkbox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <label class=\"form-check-label\">\r\n <input type=\"checkbox\" class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\"\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label class=\"form-check-label\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n <input type=\"checkbox\" class=\"form-check-input\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n </label>\r\n </div>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\" [dateFormat]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\" [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"formField?.option?.disabled\" [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete [suggestions]=\"remoteSuggestions[formField.scope?.reference] ?? []\" (completeMethod)=\"completeRemote($event, formField)\"\r\n [emptyMessage]=\"autocompleteEmptyText(formField)\" [showEmptyMessage]=\"true\" #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\" [dropdown]=\"true\"\r\n [optionLabel]=\"formField.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: formField.scope?.reference\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[formField?.option?.optionValue]\">\r\n {{ item[formField?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <nz-select nzShowSearch nzAllowClear nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField?.required\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <nz-option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [nzLabel]=\"item[formField?.option?.optionLabel]\"\r\n [nzValue]=\"item[formField?.option?.optionValue]\"></nz-option>\r\n </nz-select>\r\n </div>\r\n\r\n <div *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"dropdownField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, dropdownField)\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button/default control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.style)?.style\"\r\n (click)=\"invokeFormBtnClick(formField?.option, form.propertySchema)\">\r\n {{ formField?.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n\r\n<ng-template #formMediaContent let-field=\"field\">\r\n <ng-container *ngIf=\"getMediaSource(field) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(field)\">\r\n <img *ngSwitchDefault [src]=\"source\" [alt]=\"getMediaLabel(field)\" loading=\"lazy\" />\r\n <video *ngSwitchCase=\"'video'\" [src]=\"(source | vlMedia | async) || null\"\r\n [attr.aria-label]=\"getMediaLabel(field)\" controls playsinline preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n <audio *ngSwitchCase=\"'audio'\" [src]=\"(source | vlMedia | async) || null\"\r\n [attr.aria-label]=\"getMediaLabel(field)\" controls preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n <a *ngSwitchCase=\"'pdf'\" [href]=\"source\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n <a *ngSwitchCase=\"'file'\" [href]=\"source\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>\r\n\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.form-renderer{width:100%;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:12px}.section-card{border:1px solid #e5e7eb;border-radius:var(--radius-sm, 8px);padding:12px;margin-bottom:12px;background:#fff;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;font-weight:600}.section-title{font-size:14px;color:#0f172a}.section-body{position:relative;width:100%;min-height:78px;box-sizing:border-box}.field-row{position:relative;min-width:0;width:100%;box-sizing:border-box}.field-row input,.field-row select,.field-row nz-select,.field-row textarea,.field-row button{max-width:100%;box-sizing:border-box}@media(max-width:768px){.section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}.field-row{grid-column:span 1!important}}@media(min-width:769px)and (max-width:1024px){.section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}.field-row{grid-column:span 6!important}}:host-context(.mobile-frame) .section-body,:host-context(.device-mobile) .section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}:host-context(.mobile-frame) .field-row,:host-context(.device-mobile) .field-row{grid-column:span 1!important}:host-context(.tablet-frame) .section-body,:host-context(.device-tablet) .section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}:host-context(.tablet-frame) .field-row,:host-context(.device-tablet) .field-row{grid-column:span 6!important}\n"] }]
4917
5219
  }], propDecorators: { errorMessages: [{
4918
5220
  type: Input
4919
5221
  }], creditCardError: [{
@@ -4928,6 +5230,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
4928
5230
  type: Output
4929
5231
  }] } });
4930
5232
 
5233
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-15
5234
+ // - new (#145): video and audio play through vlMedia - a file name in the field becomes a signed
5235
+ // CouchDB link (attachment, streamed with Range)
4931
5236
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
4932
5237
  // - new: fields with a backend source (option.autocomplete) load their list through
4933
5238
  // RemoteListService - autocomplete suggestions, dropdown/combobox items; the autocompletes of this
@@ -5511,11 +5816,11 @@ class GridComponent {
5511
5816
  return 'image';
5512
5817
  }
5513
5818
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: GridComponent, deps: [{ token: RequestService }, { token: UtilityService }, { token: NotificationService }, { token: i4$1.OktaAuthStateService }], target: i0.ɵɵFactoryTarget.Component });
5514
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: GridComponent, isStandalone: true, selector: "vl-grid", inputs: { errorMessages: "errorMessages", environment: "environment", selectedLanguageInfo: "selectedLanguageInfo", tableitemInfo: "tableitemInfo", form: "form", isTableDataLoad: "isTableDataLoad", currenTableOptions: "currenTableOptions", projectSeleted: "projectSeleted", pageSize: "pageSize", currentPage: "currentPage", pages: "pages", userInfo: "userInfo" }, outputs: { onPageChange: "onPageChange", onPageSizeChange: "onPageSizeChange", onGridScroll: "onGridScroll", showGridLink: "showGridLink", gridButtonClick: "gridButtonClick", gridIncreamentClick: "gridIncreamentClick", gridDecreamentClick: "gridDecreamentClick", gridImgClick: "gridImgClick" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time cells use the shared picker markup (picker-field, DateTimePickerService);\r\n their PrimeNG icon opened the calendar together with the system keyboard -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: grid cells render legacy checkbox fields through the boolean control. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: @contract comments on the sorting, on the column filter and on the footer. The filter\r\n reads option.filter and never option.columnFilter, which is the mistake a layout author\r\n makes without being told -->\r\n<div *ngIf=\"!tableitemInfo.data?.length\" class=\"alert alert-warning col-md-12\"\r\n role=\"alert\">\r\n {{ tableitemInfo.layoutSchema?.noRecordMessage}}\r\n</div>\r\n<div *ngIf=\"tableitemInfo.data?.length\" class=\"reservation-container pt-5 px-3\">\r\n <nz-table #basicTable [nzData]=\"gridDisplayData\"\r\n [nzNoResult]=\"'No matching records found'\" [nzBordered]=\"true\"\r\n [nzFrontPagination]=\"currenTableOptions?.pagination?.tabs\"\r\n [style]=\"getStyleWithoutClasses(currenTableOptions?.style).style\"\r\n (scroll)=\"onScrollEvent($event)\"\r\n [nzLoading]=\"isTableDataLoad\">\r\n <thead>\r\n <!-- @contract decision: option.sortable is read here\r\n reality: nzSortFn is wired to getColumnSortFn since 2026-09-16; before that\r\n the key was decoration. The column also needs a scope.reference -\r\n without one getColumnSortFn has nothing to sort by.\r\n seen: grid.component.ts:338 -->\r\n <!-- Column headings and sorting -->\r\n <tr>\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n [nzSortFn]=\"\r\n column?.option?.sortable\r\n ? getColumnSortFn(column)\r\n : false\r\n \">\r\n\r\n {{ column?.label?.caption }}\r\n </th>\r\n </tr>\r\n\r\n <!-- @contract decision: the column filter reads option.filter, NOT option.columnFilter\r\n reality: option.filter.filtering switches it on, option.filter.defaultOperator\r\n starts the comparison (the renderer falls back to contains), and\r\n option.filter.placeholder labels the input.\r\n failure: option.columnFilter is read nowhere, here or in the runtime app. A\r\n column carrying it looks filterable in the file and is not on screen.\r\n seen: grid.component.ts:450-475; 5 uses of columnFilter across the reference\r\n projects, all of them dead -->\r\n <!-- Column filters -->\r\n <tr *ngIf=\"hasAnyColumnFilter()\" class=\"grid-filter-row\">\r\n\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n class=\"grid-filter-cell\">\r\n\r\n <ng-container *ngIf=\"\r\n column?.option?.filter?.filtering === true &&\r\n column?.scope?.reference\r\n \">\r\n\r\n <div class=\"grid-filter-input-wrapper\">\r\n <input type=\"text\" class=\"grid-filter-input\"\r\n [placeholder]=\"getColumnFilterPlaceholder(column)\"\r\n [ngModel]=\"getColumnFilterState(column).value\"\r\n (ngModelChange)=\"\r\n setColumnFilterValue(column, $event)\r\n \" />\r\n\r\n <!-- Operator selection button -->\r\n <button type=\"button\"\r\n class=\"grid-filter-operator-button\" nz-dropdown\r\n nzTrigger=\"click\"\r\n [nzDropdownMenu]=\"operatorMenu\"\r\n [class.grid-filter-operator-button--active]=\"\r\n hasActiveColumnFilter(column)\r\n \" [title]=\"\r\n 'Operator: ' +\r\n getFilterOperatorLabel(\r\n getColumnFilterState(column).operator\r\n )\r\n \">\r\n\r\n <span nz-icon nzType=\"filter\"></span>\r\n </button>\r\n\r\n <!-- Clear current column filter -->\r\n <button *ngIf=\"hasActiveColumnFilter(column)\"\r\n type=\"button\" class=\"grid-filter-clear-button\"\r\n aria-label=\"Clear column filter\"\r\n title=\"Clear filter\"\r\n (click)=\"clearColumnFilter(column)\">\r\n\r\n <span nz-icon nzType=\"close\"></span>\r\n </button>\r\n </div>\r\n\r\n <!-- Operator dropdown -->\r\n <nz-dropdown-menu #operatorMenu=\"nzDropdownMenu\">\r\n <ul nz-menu class=\"grid-filter-operator-menu\">\r\n <li *ngFor=\"let operator of filterOperators\"\r\n nz-menu-item [nzSelected]=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" (click)=\"\r\n setColumnFilterOperator(\r\n column,\r\n operator.value\r\n )\r\n \">\r\n\r\n <span *ngIf=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" nz-icon nzType=\"check\">\r\n </span>\r\n\r\n {{ operator.label }}\r\n </li>\r\n </ul>\r\n </nz-dropdown-menu>\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n <!-- @contract decision: the column chain below, and what it does not carry\r\n reality: string, number, image, quantity, date, time, dropdown, boolean,\r\n autocomplete and dateRange each have their own condition. There is no\r\n branch for the credit card type.\r\n failure: a column whose type has no branch stays empty, with no error.\r\n instead: format the value in the endpoint and send a string.\r\n\r\n @contract decision: columns without scope.reference are valid\r\n reality: with option.actionColumn they render save/delete icons, otherwise a\r\n button. That is why the conditions below all guard on the reference. -->\r\n <tbody>\r\n <tr *ngFor=\"let data of basicTable.data; let i = index\">\r\n <td *ngFor=\"let col of tableitemInfo.layoutSchema.elements\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'string'\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span *ngIf=\"col.option?.link\"\r\n (click)=\"showLinkDetails(col.option, data)\">\r\n {{data[col.scope?.reference.split('/').pop()]}}\r\n </span>\r\n <span *ngIf=\"!col.option?.link\">\r\n <ng-container\r\n *ngIf=\"col?.option?.dateFormat; else string\">\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n | date:'yyyy-MM-dd' }}\r\n </ng-container>\r\n <ng-template #string>\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n }}\r\n </ng-template>\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'number'\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span\r\n *ngIf=\"col.option?.innerAction?.action == 'multiply'\">\r\n {{\r\n (data[col.option?.innerAction?.from] *\r\n data[col.option?.innerAction?.to])\r\n | number:'1.2-2'\r\n }}\r\n </span>\r\n <span *ngIf=\"!col.option?.innerAction\">\r\n {{data[col.scope?.reference.split('/').pop()] |\r\n currency:\r\n tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.currency\r\n :true }}\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n type=\"number\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <!-- Media control: also supports existing image properties -->\r\n <ng-container *ngIf=\"isMediaColumn(col)\">\r\n <ng-container\r\n *ngIf=\"getMediaSource(col, data) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(col, data)\">\r\n\r\n <img\r\n *ngSwitchDefault\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n loading=\"lazy\"\r\n (click)=\"invokeTableImageClick(col, data)\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video\r\n playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio\r\n playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [href]=\"source\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(col) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [href]=\"source\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(col) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- @contract compatibility: existing checkbox fields render as boolean. -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && (tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'boolean' || isLegacyCheckboxFieldType(tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type))\">\r\n <input type=\"checkbox\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n [name]=\"col.scope?.reference\" />\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dateRange'\">\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(col?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'date'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n [dateFormat]=\"\r\n tableitemInfo.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'time'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'autocomplete'\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[col.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, col)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [class]=\"col?.scope?.style?.classes ? col?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"col.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: col.scope?.reference\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dropdown'\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(col?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style)?.style\"\r\n [name]=\"col.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(col.scope?.reference)][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(col.scope?.reference)]?.[\r\n col.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[col?.option?.optionValue]\">\r\n {{ item[col?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!col?.scope?.reference\">\r\n <span *ngIf=\"col.option?.actionColumn\">\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.save, data)\"\r\n [nzType]=\"col.option?.save?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.save?.style\"\r\n [nzTwotoneColor]=\"'#52c41a'\"\r\n [class]=\"col.option?.save?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.save, data)\">\r\n </span>\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.delete, data)\"\r\n [nzType]=\"col.option?.delete?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.delete?.style\"\r\n [nzTwotoneColor]=\"'#FF0000'\"\r\n [class]=\"col.option?.delete?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.delete, data)\"></span>\r\n\r\n </span>\r\n <span *ngIf=\"!col.option?.actionColumn\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(col?.label?.style).style\"\r\n (click)=\"invokeGridBtnClick(col.option, data)\"\r\n [class]=\"col?.label?.style?.classes[0]\">\r\n {{ col.label?.caption }}\r\n </button>\r\n </span>\r\n </ng-container>\r\n\r\n <!-- For quantity column, display increment/decrement buttons -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'quantity'\">\r\n <div style=\"display: flex; align-items: center;\">\r\n <button pButton icon=\"pi pi-minus\"\r\n (click)=\"decrementQuantity(i,col.scope?.reference.split('/').pop(), col.option)\"\r\n [disabled]=\"data.quantity <= 0\"\r\n style=\"margin-right: 8px; height: 10px; width: 10px;\"></button>\r\n <span>{{ data[col.scope?.reference.split('/').pop()]\r\n }}</span>\r\n <button pButton icon=\"pi pi-plus\"\r\n (click)=\"incrementQuantity(i, col.scope?.reference.split('/').pop(), col.option)\"\r\n style=\"margin-left: 8px; height: 10px; width: 10px;\"></button>\r\n </div>\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </nz-table>\r\n</div>\r\n\r\n<!-- table footer -->\r\n<div *ngIf=\"tableitemInfo?.footer?.formFields && tableitemInfo.data?.length\"\r\n class=\"table-footer flex flex-column align-items-end gap-2 pt-2 mx-4 border-top-1 border-cyan-500\">\r\n <!-- @contract decision: the footer renders string and number only\r\n reality: the switch below carries those two cases and a default. It did NOT change\r\n when the columns gained their types on 2026-09-18.\r\n failure: any other type in a footer field renders empty.\r\n instead: keep aggregates numeric, or pre-format them as text in the endpoint. -->\r\n <div *ngFor=\"let tableField of tableitemInfo?.footer?.formFields\">\r\n <div\r\n [ngSwitch]=\"tableField?.scope?.reference && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)] && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()] ?\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.type : 'default'\">\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between;\">\r\n <span\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </span>\r\n <span>\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"text\" #inputField=\"ngModel\"\r\n [required]=\"tableField.option.required\"\r\n [email]=\"tableField.option.email\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"tableitemInfo.propertySchema | vlPlaceholder: tableField.scope?.reference\" />\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(tableField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['required']\">{{tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['email']\">{{(tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n <h5>{{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:tableField.option.currency :true:\r\n tableField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: tableField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(tableField.label.style).style\"\r\n (click)=\"invokeGridBtnClick(tableField.option, tableitemInfo.propertySchema)\">\r\n {{ tableField.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- bottom pagination on grid -->\r\n<div\r\n *ngIf=\"tableitemInfo.data?.length && !tableitemInfo?.layoutSchema?.pagination?.top && tableitemInfo?.layoutSchema?.pagination?.bottom\">\r\n <vl-pagination [itemInfo]=\"tableitemInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n</div>\r\n", styles: [".grid-filter-row th{padding:8px;vertical-align:middle;background:#fafafa}.grid-filter-cell{min-width:150px}.grid-filter-input-wrapper{display:flex;align-items:stretch;width:100%}.grid-filter-input{width:100%;min-width:0;min-height:34px;padding:5px 8px;border:1px solid #d9d9d9;border-right:0;border-radius:4px 0 0 4px;outline:none}.grid-filter-input:focus{border-color:#1677ff}.grid-filter-operator-button,.grid-filter-clear-button{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:34px;padding:0;cursor:pointer;background:#fff;border:1px solid #d9d9d9}.grid-filter-operator-button{color:#595959;border-radius:0 4px 4px 0}.grid-filter-operator-button:hover,.grid-filter-operator-button--active{color:#1677ff;border-color:#1677ff}.grid-filter-clear-button{margin-left:5px;color:#ff4d4f;border-radius:4px}.grid-filter-clear-button:hover{color:#fff;background:#ff4d4f;border-color:#ff4d4f}.grid-filter-operator-menu{min-width:190px}:host ::ng-deep .ant-table-content.has-more-right{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 36px),transparent)}:host ::ng-deep .ant-table-content.has-more-left{-webkit-mask-image:linear-gradient(to left,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to left,#000 calc(100% - 36px),transparent)}:host ::ng-deep .ant-table-content.has-more-left.has-more-right{-webkit-mask-image:linear-gradient(to right,transparent,#000 36px,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to right,transparent,#000 36px,#000 calc(100% - 36px),transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NzTableModule }, { kind: "component", type: i7$1.NzTableComponent, selector: "nz-table", inputs: ["nzTableLayout", "nzShowTotal", "nzItemRender", "nzTitle", "nzFooter", "nzNoResult", "nzPageSizeOptions", "nzVirtualItemSize", "nzVirtualMaxBufferPx", "nzVirtualMinBufferPx", "nzVirtualForTrackBy", "nzLoadingDelay", "nzPageIndex", "nzPageSize", "nzTotal", "nzWidthConfig", "nzData", "nzCustomColumn", "nzPaginationPosition", "nzScroll", "noDataVirtualHeight", "nzPaginationType", "nzFrontPagination", "nzTemplateMode", "nzShowPagination", "nzLoading", "nzOuterBordered", "nzLoadingIndicator", "nzBordered", "nzSize", "nzShowSizeChanger", "nzHideOnSinglePage", "nzShowQuickJumper", "nzSimple"], outputs: ["nzPageSizeChange", "nzPageIndexChange", "nzQueryParams", "nzCurrentPageDataChange", "nzCustomColumnChange"], exportAs: ["nzTable"] }, { kind: "component", type: i7$1.NzThAddOnComponent, selector: "th[nzColumnKey], th[nzSortFn], th[nzSortOrder], th[nzFilters], th[nzShowSort], th[nzShowFilter], th[nzCustomFilter]", inputs: ["nzColumnKey", "nzFilterMultiple", "nzSortOrder", "nzSortPriority", "nzSortDirections", "nzFilters", "nzSortFn", "nzFilterFn", "nzShowSort", "nzShowFilter", "nzCustomFilter"], outputs: ["nzCheckedChange", "nzSortOrderChange", "nzFilterChange"] }, { kind: "directive", type: i7$1.NzTableCellDirective, selector: "th:not(.nz-disable-th), td:not(.nz-disable-td)" }, { kind: "directive", type: i7$1.NzThMeasureDirective, selector: "th", inputs: ["nzWidth", "colspan", "colSpan", "rowspan", "rowSpan"] }, { kind: "component", type: i7$1.NzTheadComponent, selector: "thead:not(.ant-table-thead)", outputs: ["nzSortOrderChange"] }, { kind: "component", type: i7$1.NzTbodyComponent, selector: "tbody" }, { kind: "directive", type: i7$1.NzTrDirective, selector: "tr:not([nz-table-measure-row]):not([nzExpand]):not([nz-table-fixed-row])" }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i9.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: NzDropDownModule }, { kind: "directive", type: i11$1.NzMenuDirective, selector: "[nz-menu]", inputs: ["nzInlineIndent", "nzTheme", "nzMode", "nzInlineCollapsed", "nzSelectable"], outputs: ["nzClick"], exportAs: ["nzMenu"] }, { kind: "component", type: i11$1.NzMenuItemComponent, selector: "[nz-menu-item]", inputs: ["nzPaddingLeft", "nzDisabled", "nzSelected", "nzDanger", "nzMatchRouterExact", "nzMatchRouter"], exportAs: ["nzMenuItem"] }, { kind: "directive", type: i11$2.NzDropdownDirective, selector: "[nz-dropdown]", inputs: ["nzDropdownMenu", "nzTrigger", "nzMatchWidthElement", "nzBackdrop", "nzClickHide", "nzDisabled", "nzVisible", "nzArrow", "nzOverlayClassName", "nzOverlayStyle", "nzPlacement"], outputs: ["nzVisibleChange"], exportAs: ["nzDropdown"] }, { kind: "component", type: i11$2.NzDropdownMenuComponent, selector: "nz-dropdown-menu", exportAs: ["nzDropdownMenu"] }, { kind: "ngmodule", type: NzMenuModule }, { kind: "component", type: PaginationComponent, selector: "vl-pagination", inputs: ["itemInfo", "pageSize", "currentPage", "selectedLanguageInfo", "pages"], outputs: ["pageSizeChange", "onGoToPage"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: PlaceholderPipe, name: "vlPlaceholder" }, { kind: "pipe", type: i1$1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] });
5819
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: GridComponent, isStandalone: true, selector: "vl-grid", inputs: { errorMessages: "errorMessages", environment: "environment", selectedLanguageInfo: "selectedLanguageInfo", tableitemInfo: "tableitemInfo", form: "form", isTableDataLoad: "isTableDataLoad", currenTableOptions: "currenTableOptions", projectSeleted: "projectSeleted", pageSize: "pageSize", currentPage: "currentPage", pages: "pages", userInfo: "userInfo" }, outputs: { onPageChange: "onPageChange", onPageSizeChange: "onPageSizeChange", onGridScroll: "onGridScroll", showGridLink: "showGridLink", gridButtonClick: "gridButtonClick", gridIncreamentClick: "gridIncreamentClick", gridDecreamentClick: "gridDecreamentClick", gridImgClick: "gridImgClick" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time cells use the shared picker markup (picker-field, DateTimePickerService);\r\n their PrimeNG icon opened the calendar together with the system keyboard -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: grid cells render legacy checkbox fields through the boolean control. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: @contract comments on the sorting, on the column filter and on the footer. The filter\r\n reads option.filter and never option.columnFilter, which is the mistake a layout author\r\n makes without being told -->\r\n<div *ngIf=\"!tableitemInfo.data?.length\" class=\"alert alert-warning col-md-12\"\r\n role=\"alert\">\r\n {{ tableitemInfo.layoutSchema?.noRecordMessage}}\r\n</div>\r\n<div *ngIf=\"tableitemInfo.data?.length\" class=\"reservation-container pt-5 px-3\">\r\n <nz-table #basicTable [nzData]=\"gridDisplayData\"\r\n [nzNoResult]=\"'No matching records found'\" [nzBordered]=\"true\"\r\n [nzFrontPagination]=\"currenTableOptions?.pagination?.tabs\"\r\n [style]=\"getStyleWithoutClasses(currenTableOptions?.style).style\"\r\n (scroll)=\"onScrollEvent($event)\"\r\n [nzLoading]=\"isTableDataLoad\">\r\n <thead>\r\n <!-- @contract decision: option.sortable is read here\r\n reality: nzSortFn is wired to getColumnSortFn since 2026-09-16; before that\r\n the key was decoration. The column also needs a scope.reference -\r\n without one getColumnSortFn has nothing to sort by.\r\n seen: grid.component.ts:338 -->\r\n <!-- Column headings and sorting -->\r\n <tr>\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n [nzSortFn]=\"\r\n column?.option?.sortable\r\n ? getColumnSortFn(column)\r\n : false\r\n \">\r\n\r\n {{ column?.label?.caption }}\r\n </th>\r\n </tr>\r\n\r\n <!-- @contract decision: the column filter reads option.filter, NOT option.columnFilter\r\n reality: option.filter.filtering switches it on, option.filter.defaultOperator\r\n starts the comparison (the renderer falls back to contains), and\r\n option.filter.placeholder labels the input.\r\n failure: option.columnFilter is read nowhere, here or in the runtime app. A\r\n column carrying it looks filterable in the file and is not on screen.\r\n seen: grid.component.ts:450-475; 5 uses of columnFilter across the reference\r\n projects, all of them dead -->\r\n <!-- Column filters -->\r\n <tr *ngIf=\"hasAnyColumnFilter()\" class=\"grid-filter-row\">\r\n\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n class=\"grid-filter-cell\">\r\n\r\n <ng-container *ngIf=\"\r\n column?.option?.filter?.filtering === true &&\r\n column?.scope?.reference\r\n \">\r\n\r\n <div class=\"grid-filter-input-wrapper\">\r\n <input type=\"text\" class=\"grid-filter-input\"\r\n [placeholder]=\"getColumnFilterPlaceholder(column)\"\r\n [ngModel]=\"getColumnFilterState(column).value\"\r\n (ngModelChange)=\"\r\n setColumnFilterValue(column, $event)\r\n \" />\r\n\r\n <!-- Operator selection button -->\r\n <button type=\"button\"\r\n class=\"grid-filter-operator-button\" nz-dropdown\r\n nzTrigger=\"click\"\r\n [nzDropdownMenu]=\"operatorMenu\"\r\n [class.grid-filter-operator-button--active]=\"\r\n hasActiveColumnFilter(column)\r\n \" [title]=\"\r\n 'Operator: ' +\r\n getFilterOperatorLabel(\r\n getColumnFilterState(column).operator\r\n )\r\n \">\r\n\r\n <span nz-icon nzType=\"filter\"></span>\r\n </button>\r\n\r\n <!-- Clear current column filter -->\r\n <button *ngIf=\"hasActiveColumnFilter(column)\"\r\n type=\"button\" class=\"grid-filter-clear-button\"\r\n aria-label=\"Clear column filter\"\r\n title=\"Clear filter\"\r\n (click)=\"clearColumnFilter(column)\">\r\n\r\n <span nz-icon nzType=\"close\"></span>\r\n </button>\r\n </div>\r\n\r\n <!-- Operator dropdown -->\r\n <nz-dropdown-menu #operatorMenu=\"nzDropdownMenu\">\r\n <ul nz-menu class=\"grid-filter-operator-menu\">\r\n <li *ngFor=\"let operator of filterOperators\"\r\n nz-menu-item [nzSelected]=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" (click)=\"\r\n setColumnFilterOperator(\r\n column,\r\n operator.value\r\n )\r\n \">\r\n\r\n <span *ngIf=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" nz-icon nzType=\"check\">\r\n </span>\r\n\r\n {{ operator.label }}\r\n </li>\r\n </ul>\r\n </nz-dropdown-menu>\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n <!-- @contract decision: the column chain below, and what it does not carry\r\n reality: string, number, image, quantity, date, time, dropdown, boolean,\r\n autocomplete and dateRange each have their own condition. There is no\r\n branch for the credit card type.\r\n failure: a column whose type has no branch stays empty, with no error.\r\n instead: format the value in the endpoint and send a string.\r\n\r\n @contract decision: columns without scope.reference are valid\r\n reality: with option.actionColumn they render save/delete icons, otherwise a\r\n button. That is why the conditions below all guard on the reference. -->\r\n <tbody>\r\n <tr *ngFor=\"let data of basicTable.data; let i = index\">\r\n <td *ngFor=\"let col of tableitemInfo.layoutSchema.elements\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'string'\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span *ngIf=\"col.option?.link\"\r\n (click)=\"showLinkDetails(col.option, data)\">\r\n {{data[col.scope?.reference.split('/').pop()]}}\r\n </span>\r\n <span *ngIf=\"!col.option?.link\">\r\n <ng-container\r\n *ngIf=\"col?.option?.dateFormat; else string\">\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n | date:'yyyy-MM-dd' }}\r\n </ng-container>\r\n <ng-template #string>\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n }}\r\n </ng-template>\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'number'\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span\r\n *ngIf=\"col.option?.innerAction?.action == 'multiply'\">\r\n {{\r\n (data[col.option?.innerAction?.from] *\r\n data[col.option?.innerAction?.to])\r\n | number:'1.2-2'\r\n }}\r\n </span>\r\n <span *ngIf=\"!col.option?.innerAction\">\r\n {{data[col.scope?.reference.split('/').pop()] |\r\n currency:\r\n tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.currency\r\n :true }}\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n type=\"number\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <!-- Media control: also supports existing image properties -->\r\n <ng-container *ngIf=\"isMediaColumn(col)\">\r\n <ng-container\r\n *ngIf=\"getMediaSource(col, data) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(col, data)\">\r\n\r\n <img\r\n *ngSwitchDefault\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n loading=\"lazy\"\r\n (click)=\"invokeTableImageClick(col, data)\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [src]=\"(source | vlMedia | async) || null\"\r\n [attr.aria-label]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video\r\n playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [src]=\"(source | vlMedia | async) || null\"\r\n [attr.aria-label]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio\r\n playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [href]=\"source\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(col) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [href]=\"source\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(col) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- @contract compatibility: existing checkbox fields render as boolean. -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && (tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'boolean' || isLegacyCheckboxFieldType(tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type))\">\r\n <input type=\"checkbox\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n [name]=\"col.scope?.reference\" />\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dateRange'\">\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(col?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'date'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n [dateFormat]=\"\r\n tableitemInfo.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'time'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'autocomplete'\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[col.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, col)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [class]=\"col?.scope?.style?.classes ? col?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"col.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: col.scope?.reference\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dropdown'\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(col?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style)?.style\"\r\n [name]=\"col.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(col.scope?.reference)][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(col.scope?.reference)]?.[\r\n col.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[col?.option?.optionValue]\">\r\n {{ item[col?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!col?.scope?.reference\">\r\n <span *ngIf=\"col.option?.actionColumn\">\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.save, data)\"\r\n [nzType]=\"col.option?.save?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.save?.style\"\r\n [nzTwotoneColor]=\"'#52c41a'\"\r\n [class]=\"col.option?.save?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.save, data)\">\r\n </span>\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.delete, data)\"\r\n [nzType]=\"col.option?.delete?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.delete?.style\"\r\n [nzTwotoneColor]=\"'#FF0000'\"\r\n [class]=\"col.option?.delete?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.delete, data)\"></span>\r\n\r\n </span>\r\n <span *ngIf=\"!col.option?.actionColumn\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(col?.label?.style).style\"\r\n (click)=\"invokeGridBtnClick(col.option, data)\"\r\n [class]=\"col?.label?.style?.classes[0]\">\r\n {{ col.label?.caption }}\r\n </button>\r\n </span>\r\n </ng-container>\r\n\r\n <!-- For quantity column, display increment/decrement buttons -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'quantity'\">\r\n <div style=\"display: flex; align-items: center;\">\r\n <button pButton icon=\"pi pi-minus\"\r\n (click)=\"decrementQuantity(i,col.scope?.reference.split('/').pop(), col.option)\"\r\n [disabled]=\"data.quantity <= 0\"\r\n style=\"margin-right: 8px; height: 10px; width: 10px;\"></button>\r\n <span>{{ data[col.scope?.reference.split('/').pop()]\r\n }}</span>\r\n <button pButton icon=\"pi pi-plus\"\r\n (click)=\"incrementQuantity(i, col.scope?.reference.split('/').pop(), col.option)\"\r\n style=\"margin-left: 8px; height: 10px; width: 10px;\"></button>\r\n </div>\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </nz-table>\r\n</div>\r\n\r\n<!-- table footer -->\r\n<div *ngIf=\"tableitemInfo?.footer?.formFields && tableitemInfo.data?.length\"\r\n class=\"table-footer flex flex-column align-items-end gap-2 pt-2 mx-4 border-top-1 border-cyan-500\">\r\n <!-- @contract decision: the footer renders string and number only\r\n reality: the switch below carries those two cases and a default. It did NOT change\r\n when the columns gained their types on 2026-09-18.\r\n failure: any other type in a footer field renders empty.\r\n instead: keep aggregates numeric, or pre-format them as text in the endpoint. -->\r\n <div *ngFor=\"let tableField of tableitemInfo?.footer?.formFields\">\r\n <div\r\n [ngSwitch]=\"tableField?.scope?.reference && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)] && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()] ?\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.type : 'default'\">\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between;\">\r\n <span\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </span>\r\n <span>\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"text\" #inputField=\"ngModel\"\r\n [required]=\"tableField.option.required\"\r\n [email]=\"tableField.option.email\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"tableitemInfo.propertySchema | vlPlaceholder: tableField.scope?.reference\" />\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(tableField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['required']\">{{tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['email']\">{{(tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n <h5>{{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:tableField.option.currency :true:\r\n tableField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: tableField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(tableField.label.style).style\"\r\n (click)=\"invokeGridBtnClick(tableField.option, tableitemInfo.propertySchema)\">\r\n {{ tableField.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- bottom pagination on grid -->\r\n<div\r\n *ngIf=\"tableitemInfo.data?.length && !tableitemInfo?.layoutSchema?.pagination?.top && tableitemInfo?.layoutSchema?.pagination?.bottom\">\r\n <vl-pagination [itemInfo]=\"tableitemInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n</div>\r\n", styles: [".grid-filter-row th{padding:8px;vertical-align:middle;background:#fafafa}.grid-filter-cell{min-width:150px}.grid-filter-input-wrapper{display:flex;align-items:stretch;width:100%}.grid-filter-input{width:100%;min-width:0;min-height:34px;padding:5px 8px;border:1px solid #d9d9d9;border-right:0;border-radius:4px 0 0 4px;outline:none}.grid-filter-input:focus{border-color:#1677ff}.grid-filter-operator-button,.grid-filter-clear-button{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:34px;padding:0;cursor:pointer;background:#fff;border:1px solid #d9d9d9}.grid-filter-operator-button{color:#595959;border-radius:0 4px 4px 0}.grid-filter-operator-button:hover,.grid-filter-operator-button--active{color:#1677ff;border-color:#1677ff}.grid-filter-clear-button{margin-left:5px;color:#ff4d4f;border-radius:4px}.grid-filter-clear-button:hover{color:#fff;background:#ff4d4f;border-color:#ff4d4f}.grid-filter-operator-menu{min-width:190px}:host ::ng-deep .ant-table-content.has-more-right{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 36px),transparent)}:host ::ng-deep .ant-table-content.has-more-left{-webkit-mask-image:linear-gradient(to left,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to left,#000 calc(100% - 36px),transparent)}:host ::ng-deep .ant-table-content.has-more-left.has-more-right{-webkit-mask-image:linear-gradient(to right,transparent,#000 36px,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to right,transparent,#000 36px,#000 calc(100% - 36px),transparent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i5.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NzTableModule }, { kind: "component", type: i7$1.NzTableComponent, selector: "nz-table", inputs: ["nzTableLayout", "nzShowTotal", "nzItemRender", "nzTitle", "nzFooter", "nzNoResult", "nzPageSizeOptions", "nzVirtualItemSize", "nzVirtualMaxBufferPx", "nzVirtualMinBufferPx", "nzVirtualForTrackBy", "nzLoadingDelay", "nzPageIndex", "nzPageSize", "nzTotal", "nzWidthConfig", "nzData", "nzCustomColumn", "nzPaginationPosition", "nzScroll", "noDataVirtualHeight", "nzPaginationType", "nzFrontPagination", "nzTemplateMode", "nzShowPagination", "nzLoading", "nzOuterBordered", "nzLoadingIndicator", "nzBordered", "nzSize", "nzShowSizeChanger", "nzHideOnSinglePage", "nzShowQuickJumper", "nzSimple"], outputs: ["nzPageSizeChange", "nzPageIndexChange", "nzQueryParams", "nzCurrentPageDataChange", "nzCustomColumnChange"], exportAs: ["nzTable"] }, { kind: "component", type: i7$1.NzThAddOnComponent, selector: "th[nzColumnKey], th[nzSortFn], th[nzSortOrder], th[nzFilters], th[nzShowSort], th[nzShowFilter], th[nzCustomFilter]", inputs: ["nzColumnKey", "nzFilterMultiple", "nzSortOrder", "nzSortPriority", "nzSortDirections", "nzFilters", "nzSortFn", "nzFilterFn", "nzShowSort", "nzShowFilter", "nzCustomFilter"], outputs: ["nzCheckedChange", "nzSortOrderChange", "nzFilterChange"] }, { kind: "directive", type: i7$1.NzTableCellDirective, selector: "th:not(.nz-disable-th), td:not(.nz-disable-td)" }, { kind: "directive", type: i7$1.NzThMeasureDirective, selector: "th", inputs: ["nzWidth", "colspan", "colSpan", "rowspan", "rowSpan"] }, { kind: "component", type: i7$1.NzTheadComponent, selector: "thead:not(.ant-table-thead)", outputs: ["nzSortOrderChange"] }, { kind: "component", type: i7$1.NzTbodyComponent, selector: "tbody" }, { kind: "directive", type: i7$1.NzTrDirective, selector: "tr:not([nz-table-measure-row]):not([nzExpand]):not([nz-table-fixed-row])" }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i9.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: NzDropDownModule }, { kind: "directive", type: i11$1.NzMenuDirective, selector: "[nz-menu]", inputs: ["nzInlineIndent", "nzTheme", "nzMode", "nzInlineCollapsed", "nzSelectable"], outputs: ["nzClick"], exportAs: ["nzMenu"] }, { kind: "component", type: i11$1.NzMenuItemComponent, selector: "[nz-menu-item]", inputs: ["nzPaddingLeft", "nzDisabled", "nzSelected", "nzDanger", "nzMatchRouterExact", "nzMatchRouter"], exportAs: ["nzMenuItem"] }, { kind: "directive", type: i11$2.NzDropdownDirective, selector: "[nz-dropdown]", inputs: ["nzDropdownMenu", "nzTrigger", "nzMatchWidthElement", "nzBackdrop", "nzClickHide", "nzDisabled", "nzVisible", "nzArrow", "nzOverlayClassName", "nzOverlayStyle", "nzPlacement"], outputs: ["nzVisibleChange"], exportAs: ["nzDropdown"] }, { kind: "component", type: i11$2.NzDropdownMenuComponent, selector: "nz-dropdown-menu", exportAs: ["nzDropdownMenu"] }, { kind: "ngmodule", type: NzMenuModule }, { kind: "component", type: PaginationComponent, selector: "vl-pagination", inputs: ["itemInfo", "pageSize", "currentPage", "selectedLanguageInfo", "pages"], outputs: ["pageSizeChange", "onGoToPage"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: PlaceholderPipe, name: "vlPlaceholder" }, { kind: "pipe", type: MediaSourcePipe, name: "vlMedia" }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.DecimalPipe, name: "number" }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i5.DatePipe, name: "date" }] });
5515
5820
  }
5516
5821
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: GridComponent, decorators: [{
5517
5822
  type: Component,
5518
- args: [{ standalone: true, imports: [PlaceholderPipe,
5823
+ args: [{ standalone: true, imports: [PlaceholderPipe, MediaSourcePipe,
5519
5824
  CommonModule,
5520
5825
  FormsModule,
5521
5826
  NzTableModule,
@@ -5529,7 +5834,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
5529
5834
  AutoCompleteModule,
5530
5835
  InputTextModule,
5531
5836
  CustomKeyboardDirective,
5532
- ], selector: 'vl-grid', template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time cells use the shared picker markup (picker-field, DateTimePickerService);\r\n their PrimeNG icon opened the calendar together with the system keyboard -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: grid cells render legacy checkbox fields through the boolean control. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: @contract comments on the sorting, on the column filter and on the footer. The filter\r\n reads option.filter and never option.columnFilter, which is the mistake a layout author\r\n makes without being told -->\r\n<div *ngIf=\"!tableitemInfo.data?.length\" class=\"alert alert-warning col-md-12\"\r\n role=\"alert\">\r\n {{ tableitemInfo.layoutSchema?.noRecordMessage}}\r\n</div>\r\n<div *ngIf=\"tableitemInfo.data?.length\" class=\"reservation-container pt-5 px-3\">\r\n <nz-table #basicTable [nzData]=\"gridDisplayData\"\r\n [nzNoResult]=\"'No matching records found'\" [nzBordered]=\"true\"\r\n [nzFrontPagination]=\"currenTableOptions?.pagination?.tabs\"\r\n [style]=\"getStyleWithoutClasses(currenTableOptions?.style).style\"\r\n (scroll)=\"onScrollEvent($event)\"\r\n [nzLoading]=\"isTableDataLoad\">\r\n <thead>\r\n <!-- @contract decision: option.sortable is read here\r\n reality: nzSortFn is wired to getColumnSortFn since 2026-09-16; before that\r\n the key was decoration. The column also needs a scope.reference -\r\n without one getColumnSortFn has nothing to sort by.\r\n seen: grid.component.ts:338 -->\r\n <!-- Column headings and sorting -->\r\n <tr>\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n [nzSortFn]=\"\r\n column?.option?.sortable\r\n ? getColumnSortFn(column)\r\n : false\r\n \">\r\n\r\n {{ column?.label?.caption }}\r\n </th>\r\n </tr>\r\n\r\n <!-- @contract decision: the column filter reads option.filter, NOT option.columnFilter\r\n reality: option.filter.filtering switches it on, option.filter.defaultOperator\r\n starts the comparison (the renderer falls back to contains), and\r\n option.filter.placeholder labels the input.\r\n failure: option.columnFilter is read nowhere, here or in the runtime app. A\r\n column carrying it looks filterable in the file and is not on screen.\r\n seen: grid.component.ts:450-475; 5 uses of columnFilter across the reference\r\n projects, all of them dead -->\r\n <!-- Column filters -->\r\n <tr *ngIf=\"hasAnyColumnFilter()\" class=\"grid-filter-row\">\r\n\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n class=\"grid-filter-cell\">\r\n\r\n <ng-container *ngIf=\"\r\n column?.option?.filter?.filtering === true &&\r\n column?.scope?.reference\r\n \">\r\n\r\n <div class=\"grid-filter-input-wrapper\">\r\n <input type=\"text\" class=\"grid-filter-input\"\r\n [placeholder]=\"getColumnFilterPlaceholder(column)\"\r\n [ngModel]=\"getColumnFilterState(column).value\"\r\n (ngModelChange)=\"\r\n setColumnFilterValue(column, $event)\r\n \" />\r\n\r\n <!-- Operator selection button -->\r\n <button type=\"button\"\r\n class=\"grid-filter-operator-button\" nz-dropdown\r\n nzTrigger=\"click\"\r\n [nzDropdownMenu]=\"operatorMenu\"\r\n [class.grid-filter-operator-button--active]=\"\r\n hasActiveColumnFilter(column)\r\n \" [title]=\"\r\n 'Operator: ' +\r\n getFilterOperatorLabel(\r\n getColumnFilterState(column).operator\r\n )\r\n \">\r\n\r\n <span nz-icon nzType=\"filter\"></span>\r\n </button>\r\n\r\n <!-- Clear current column filter -->\r\n <button *ngIf=\"hasActiveColumnFilter(column)\"\r\n type=\"button\" class=\"grid-filter-clear-button\"\r\n aria-label=\"Clear column filter\"\r\n title=\"Clear filter\"\r\n (click)=\"clearColumnFilter(column)\">\r\n\r\n <span nz-icon nzType=\"close\"></span>\r\n </button>\r\n </div>\r\n\r\n <!-- Operator dropdown -->\r\n <nz-dropdown-menu #operatorMenu=\"nzDropdownMenu\">\r\n <ul nz-menu class=\"grid-filter-operator-menu\">\r\n <li *ngFor=\"let operator of filterOperators\"\r\n nz-menu-item [nzSelected]=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" (click)=\"\r\n setColumnFilterOperator(\r\n column,\r\n operator.value\r\n )\r\n \">\r\n\r\n <span *ngIf=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" nz-icon nzType=\"check\">\r\n </span>\r\n\r\n {{ operator.label }}\r\n </li>\r\n </ul>\r\n </nz-dropdown-menu>\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n <!-- @contract decision: the column chain below, and what it does not carry\r\n reality: string, number, image, quantity, date, time, dropdown, boolean,\r\n autocomplete and dateRange each have their own condition. There is no\r\n branch for the credit card type.\r\n failure: a column whose type has no branch stays empty, with no error.\r\n instead: format the value in the endpoint and send a string.\r\n\r\n @contract decision: columns without scope.reference are valid\r\n reality: with option.actionColumn they render save/delete icons, otherwise a\r\n button. That is why the conditions below all guard on the reference. -->\r\n <tbody>\r\n <tr *ngFor=\"let data of basicTable.data; let i = index\">\r\n <td *ngFor=\"let col of tableitemInfo.layoutSchema.elements\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'string'\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span *ngIf=\"col.option?.link\"\r\n (click)=\"showLinkDetails(col.option, data)\">\r\n {{data[col.scope?.reference.split('/').pop()]}}\r\n </span>\r\n <span *ngIf=\"!col.option?.link\">\r\n <ng-container\r\n *ngIf=\"col?.option?.dateFormat; else string\">\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n | date:'yyyy-MM-dd' }}\r\n </ng-container>\r\n <ng-template #string>\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n }}\r\n </ng-template>\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'number'\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span\r\n *ngIf=\"col.option?.innerAction?.action == 'multiply'\">\r\n {{\r\n (data[col.option?.innerAction?.from] *\r\n data[col.option?.innerAction?.to])\r\n | number:'1.2-2'\r\n }}\r\n </span>\r\n <span *ngIf=\"!col.option?.innerAction\">\r\n {{data[col.scope?.reference.split('/').pop()] |\r\n currency:\r\n tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.currency\r\n :true }}\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n type=\"number\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <!-- Media control: also supports existing image properties -->\r\n <ng-container *ngIf=\"isMediaColumn(col)\">\r\n <ng-container\r\n *ngIf=\"getMediaSource(col, data) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(col, data)\">\r\n\r\n <img\r\n *ngSwitchDefault\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n loading=\"lazy\"\r\n (click)=\"invokeTableImageClick(col, data)\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video\r\n playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio\r\n playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [href]=\"source\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(col) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [href]=\"source\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(col) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- @contract compatibility: existing checkbox fields render as boolean. -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && (tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'boolean' || isLegacyCheckboxFieldType(tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type))\">\r\n <input type=\"checkbox\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n [name]=\"col.scope?.reference\" />\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dateRange'\">\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(col?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'date'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n [dateFormat]=\"\r\n tableitemInfo.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'time'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'autocomplete'\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[col.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, col)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [class]=\"col?.scope?.style?.classes ? col?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"col.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: col.scope?.reference\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dropdown'\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(col?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style)?.style\"\r\n [name]=\"col.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(col.scope?.reference)][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(col.scope?.reference)]?.[\r\n col.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[col?.option?.optionValue]\">\r\n {{ item[col?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!col?.scope?.reference\">\r\n <span *ngIf=\"col.option?.actionColumn\">\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.save, data)\"\r\n [nzType]=\"col.option?.save?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.save?.style\"\r\n [nzTwotoneColor]=\"'#52c41a'\"\r\n [class]=\"col.option?.save?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.save, data)\">\r\n </span>\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.delete, data)\"\r\n [nzType]=\"col.option?.delete?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.delete?.style\"\r\n [nzTwotoneColor]=\"'#FF0000'\"\r\n [class]=\"col.option?.delete?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.delete, data)\"></span>\r\n\r\n </span>\r\n <span *ngIf=\"!col.option?.actionColumn\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(col?.label?.style).style\"\r\n (click)=\"invokeGridBtnClick(col.option, data)\"\r\n [class]=\"col?.label?.style?.classes[0]\">\r\n {{ col.label?.caption }}\r\n </button>\r\n </span>\r\n </ng-container>\r\n\r\n <!-- For quantity column, display increment/decrement buttons -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'quantity'\">\r\n <div style=\"display: flex; align-items: center;\">\r\n <button pButton icon=\"pi pi-minus\"\r\n (click)=\"decrementQuantity(i,col.scope?.reference.split('/').pop(), col.option)\"\r\n [disabled]=\"data.quantity <= 0\"\r\n style=\"margin-right: 8px; height: 10px; width: 10px;\"></button>\r\n <span>{{ data[col.scope?.reference.split('/').pop()]\r\n }}</span>\r\n <button pButton icon=\"pi pi-plus\"\r\n (click)=\"incrementQuantity(i, col.scope?.reference.split('/').pop(), col.option)\"\r\n style=\"margin-left: 8px; height: 10px; width: 10px;\"></button>\r\n </div>\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </nz-table>\r\n</div>\r\n\r\n<!-- table footer -->\r\n<div *ngIf=\"tableitemInfo?.footer?.formFields && tableitemInfo.data?.length\"\r\n class=\"table-footer flex flex-column align-items-end gap-2 pt-2 mx-4 border-top-1 border-cyan-500\">\r\n <!-- @contract decision: the footer renders string and number only\r\n reality: the switch below carries those two cases and a default. It did NOT change\r\n when the columns gained their types on 2026-09-18.\r\n failure: any other type in a footer field renders empty.\r\n instead: keep aggregates numeric, or pre-format them as text in the endpoint. -->\r\n <div *ngFor=\"let tableField of tableitemInfo?.footer?.formFields\">\r\n <div\r\n [ngSwitch]=\"tableField?.scope?.reference && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)] && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()] ?\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.type : 'default'\">\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between;\">\r\n <span\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </span>\r\n <span>\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"text\" #inputField=\"ngModel\"\r\n [required]=\"tableField.option.required\"\r\n [email]=\"tableField.option.email\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"tableitemInfo.propertySchema | vlPlaceholder: tableField.scope?.reference\" />\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(tableField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['required']\">{{tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['email']\">{{(tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n <h5>{{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:tableField.option.currency :true:\r\n tableField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: tableField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(tableField.label.style).style\"\r\n (click)=\"invokeGridBtnClick(tableField.option, tableitemInfo.propertySchema)\">\r\n {{ tableField.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- bottom pagination on grid -->\r\n<div\r\n *ngIf=\"tableitemInfo.data?.length && !tableitemInfo?.layoutSchema?.pagination?.top && tableitemInfo?.layoutSchema?.pagination?.bottom\">\r\n <vl-pagination [itemInfo]=\"tableitemInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n</div>\r\n", styles: [".grid-filter-row th{padding:8px;vertical-align:middle;background:#fafafa}.grid-filter-cell{min-width:150px}.grid-filter-input-wrapper{display:flex;align-items:stretch;width:100%}.grid-filter-input{width:100%;min-width:0;min-height:34px;padding:5px 8px;border:1px solid #d9d9d9;border-right:0;border-radius:4px 0 0 4px;outline:none}.grid-filter-input:focus{border-color:#1677ff}.grid-filter-operator-button,.grid-filter-clear-button{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:34px;padding:0;cursor:pointer;background:#fff;border:1px solid #d9d9d9}.grid-filter-operator-button{color:#595959;border-radius:0 4px 4px 0}.grid-filter-operator-button:hover,.grid-filter-operator-button--active{color:#1677ff;border-color:#1677ff}.grid-filter-clear-button{margin-left:5px;color:#ff4d4f;border-radius:4px}.grid-filter-clear-button:hover{color:#fff;background:#ff4d4f;border-color:#ff4d4f}.grid-filter-operator-menu{min-width:190px}:host ::ng-deep .ant-table-content.has-more-right{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 36px),transparent)}:host ::ng-deep .ant-table-content.has-more-left{-webkit-mask-image:linear-gradient(to left,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to left,#000 calc(100% - 36px),transparent)}:host ::ng-deep .ant-table-content.has-more-left.has-more-right{-webkit-mask-image:linear-gradient(to right,transparent,#000 36px,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to right,transparent,#000 36px,#000 calc(100% - 36px),transparent)}\n"] }]
5837
+ ], selector: 'vl-grid', template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: autocompletes get their suggestions from RemoteListService (completeRemote) -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time cells use the shared picker markup (picker-field, DateTimePickerService);\r\n their PrimeNG icon opened the calendar together with the system keyboard -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: grid cells render legacy checkbox fields through the boolean control. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: @contract comments on the sorting, on the column filter and on the footer. The filter\r\n reads option.filter and never option.columnFilter, which is the mistake a layout author\r\n makes without being told -->\r\n<div *ngIf=\"!tableitemInfo.data?.length\" class=\"alert alert-warning col-md-12\"\r\n role=\"alert\">\r\n {{ tableitemInfo.layoutSchema?.noRecordMessage}}\r\n</div>\r\n<div *ngIf=\"tableitemInfo.data?.length\" class=\"reservation-container pt-5 px-3\">\r\n <nz-table #basicTable [nzData]=\"gridDisplayData\"\r\n [nzNoResult]=\"'No matching records found'\" [nzBordered]=\"true\"\r\n [nzFrontPagination]=\"currenTableOptions?.pagination?.tabs\"\r\n [style]=\"getStyleWithoutClasses(currenTableOptions?.style).style\"\r\n (scroll)=\"onScrollEvent($event)\"\r\n [nzLoading]=\"isTableDataLoad\">\r\n <thead>\r\n <!-- @contract decision: option.sortable is read here\r\n reality: nzSortFn is wired to getColumnSortFn since 2026-09-16; before that\r\n the key was decoration. The column also needs a scope.reference -\r\n without one getColumnSortFn has nothing to sort by.\r\n seen: grid.component.ts:338 -->\r\n <!-- Column headings and sorting -->\r\n <tr>\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n [nzSortFn]=\"\r\n column?.option?.sortable\r\n ? getColumnSortFn(column)\r\n : false\r\n \">\r\n\r\n {{ column?.label?.caption }}\r\n </th>\r\n </tr>\r\n\r\n <!-- @contract decision: the column filter reads option.filter, NOT option.columnFilter\r\n reality: option.filter.filtering switches it on, option.filter.defaultOperator\r\n starts the comparison (the renderer falls back to contains), and\r\n option.filter.placeholder labels the input.\r\n failure: option.columnFilter is read nowhere, here or in the runtime app. A\r\n column carrying it looks filterable in the file and is not on screen.\r\n seen: grid.component.ts:450-475; 5 uses of columnFilter across the reference\r\n projects, all of them dead -->\r\n <!-- Column filters -->\r\n <tr *ngIf=\"hasAnyColumnFilter()\" class=\"grid-filter-row\">\r\n\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n class=\"grid-filter-cell\">\r\n\r\n <ng-container *ngIf=\"\r\n column?.option?.filter?.filtering === true &&\r\n column?.scope?.reference\r\n \">\r\n\r\n <div class=\"grid-filter-input-wrapper\">\r\n <input type=\"text\" class=\"grid-filter-input\"\r\n [placeholder]=\"getColumnFilterPlaceholder(column)\"\r\n [ngModel]=\"getColumnFilterState(column).value\"\r\n (ngModelChange)=\"\r\n setColumnFilterValue(column, $event)\r\n \" />\r\n\r\n <!-- Operator selection button -->\r\n <button type=\"button\"\r\n class=\"grid-filter-operator-button\" nz-dropdown\r\n nzTrigger=\"click\"\r\n [nzDropdownMenu]=\"operatorMenu\"\r\n [class.grid-filter-operator-button--active]=\"\r\n hasActiveColumnFilter(column)\r\n \" [title]=\"\r\n 'Operator: ' +\r\n getFilterOperatorLabel(\r\n getColumnFilterState(column).operator\r\n )\r\n \">\r\n\r\n <span nz-icon nzType=\"filter\"></span>\r\n </button>\r\n\r\n <!-- Clear current column filter -->\r\n <button *ngIf=\"hasActiveColumnFilter(column)\"\r\n type=\"button\" class=\"grid-filter-clear-button\"\r\n aria-label=\"Clear column filter\"\r\n title=\"Clear filter\"\r\n (click)=\"clearColumnFilter(column)\">\r\n\r\n <span nz-icon nzType=\"close\"></span>\r\n </button>\r\n </div>\r\n\r\n <!-- Operator dropdown -->\r\n <nz-dropdown-menu #operatorMenu=\"nzDropdownMenu\">\r\n <ul nz-menu class=\"grid-filter-operator-menu\">\r\n <li *ngFor=\"let operator of filterOperators\"\r\n nz-menu-item [nzSelected]=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" (click)=\"\r\n setColumnFilterOperator(\r\n column,\r\n operator.value\r\n )\r\n \">\r\n\r\n <span *ngIf=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" nz-icon nzType=\"check\">\r\n </span>\r\n\r\n {{ operator.label }}\r\n </li>\r\n </ul>\r\n </nz-dropdown-menu>\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n <!-- @contract decision: the column chain below, and what it does not carry\r\n reality: string, number, image, quantity, date, time, dropdown, boolean,\r\n autocomplete and dateRange each have their own condition. There is no\r\n branch for the credit card type.\r\n failure: a column whose type has no branch stays empty, with no error.\r\n instead: format the value in the endpoint and send a string.\r\n\r\n @contract decision: columns without scope.reference are valid\r\n reality: with option.actionColumn they render save/delete icons, otherwise a\r\n button. That is why the conditions below all guard on the reference. -->\r\n <tbody>\r\n <tr *ngFor=\"let data of basicTable.data; let i = index\">\r\n <td *ngFor=\"let col of tableitemInfo.layoutSchema.elements\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'string'\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span *ngIf=\"col.option?.link\"\r\n (click)=\"showLinkDetails(col.option, data)\">\r\n {{data[col.scope?.reference.split('/').pop()]}}\r\n </span>\r\n <span *ngIf=\"!col.option?.link\">\r\n <ng-container\r\n *ngIf=\"col?.option?.dateFormat; else string\">\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n | date:'yyyy-MM-dd' }}\r\n </ng-container>\r\n <ng-template #string>\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n }}\r\n </ng-template>\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'number'\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span\r\n *ngIf=\"col.option?.innerAction?.action == 'multiply'\">\r\n {{\r\n (data[col.option?.innerAction?.from] *\r\n data[col.option?.innerAction?.to])\r\n | number:'1.2-2'\r\n }}\r\n </span>\r\n <span *ngIf=\"!col.option?.innerAction\">\r\n {{data[col.scope?.reference.split('/').pop()] |\r\n currency:\r\n tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.currency\r\n :true }}\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n type=\"number\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <!-- Media control: also supports existing image properties -->\r\n <ng-container *ngIf=\"isMediaColumn(col)\">\r\n <ng-container\r\n *ngIf=\"getMediaSource(col, data) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(col, data)\">\r\n\r\n <img\r\n *ngSwitchDefault\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n loading=\"lazy\"\r\n (click)=\"invokeTableImageClick(col, data)\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [src]=\"(source | vlMedia | async) || null\"\r\n [attr.aria-label]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video\r\n playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [src]=\"(source | vlMedia | async) || null\"\r\n [attr.aria-label]=\"getMediaLabel(col)\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio\r\n playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [href]=\"source\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(col) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [href]=\"source\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(col) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- @contract compatibility: existing checkbox fields render as boolean. -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && (tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'boolean' || isLegacyCheckboxFieldType(tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type))\">\r\n <input type=\"checkbox\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n [name]=\"col.scope?.reference\" />\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dateRange'\">\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(col?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'date'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n [dateFormat]=\"\r\n tableitemInfo.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'time'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'autocomplete'\">\r\n <p-autocomplete\r\n [suggestions]=\"remoteSuggestions[col.scope?.reference] ?? []\"\r\n (completeMethod)=\"completeRemote($event, col)\"\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [class]=\"col?.scope?.style?.classes ? col?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"col.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: col.scope?.reference\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dropdown'\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(col?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style)?.style\"\r\n [name]=\"col.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(col.scope?.reference)][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(col.scope?.reference)]?.[\r\n col.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[col?.option?.optionValue]\">\r\n {{ item[col?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!col?.scope?.reference\">\r\n <span *ngIf=\"col.option?.actionColumn\">\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.save, data)\"\r\n [nzType]=\"col.option?.save?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.save?.style\"\r\n [nzTwotoneColor]=\"'#52c41a'\"\r\n [class]=\"col.option?.save?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.save, data)\">\r\n </span>\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.delete, data)\"\r\n [nzType]=\"col.option?.delete?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.delete?.style\"\r\n [nzTwotoneColor]=\"'#FF0000'\"\r\n [class]=\"col.option?.delete?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.delete, data)\"></span>\r\n\r\n </span>\r\n <span *ngIf=\"!col.option?.actionColumn\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(col?.label?.style).style\"\r\n (click)=\"invokeGridBtnClick(col.option, data)\"\r\n [class]=\"col?.label?.style?.classes[0]\">\r\n {{ col.label?.caption }}\r\n </button>\r\n </span>\r\n </ng-container>\r\n\r\n <!-- For quantity column, display increment/decrement buttons -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'quantity'\">\r\n <div style=\"display: flex; align-items: center;\">\r\n <button pButton icon=\"pi pi-minus\"\r\n (click)=\"decrementQuantity(i,col.scope?.reference.split('/').pop(), col.option)\"\r\n [disabled]=\"data.quantity <= 0\"\r\n style=\"margin-right: 8px; height: 10px; width: 10px;\"></button>\r\n <span>{{ data[col.scope?.reference.split('/').pop()]\r\n }}</span>\r\n <button pButton icon=\"pi pi-plus\"\r\n (click)=\"incrementQuantity(i, col.scope?.reference.split('/').pop(), col.option)\"\r\n style=\"margin-left: 8px; height: 10px; width: 10px;\"></button>\r\n </div>\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </nz-table>\r\n</div>\r\n\r\n<!-- table footer -->\r\n<div *ngIf=\"tableitemInfo?.footer?.formFields && tableitemInfo.data?.length\"\r\n class=\"table-footer flex flex-column align-items-end gap-2 pt-2 mx-4 border-top-1 border-cyan-500\">\r\n <!-- @contract decision: the footer renders string and number only\r\n reality: the switch below carries those two cases and a default. It did NOT change\r\n when the columns gained their types on 2026-09-18.\r\n failure: any other type in a footer field renders empty.\r\n instead: keep aggregates numeric, or pre-format them as text in the endpoint. -->\r\n <div *ngFor=\"let tableField of tableitemInfo?.footer?.formFields\">\r\n <div\r\n [ngSwitch]=\"tableField?.scope?.reference && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)] && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()] ?\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.type : 'default'\">\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between;\">\r\n <span\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </span>\r\n <span>\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"text\" #inputField=\"ngModel\"\r\n [required]=\"tableField.option.required\"\r\n [email]=\"tableField.option.email\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"tableitemInfo.propertySchema | vlPlaceholder: tableField.scope?.reference\" />\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(tableField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['required']\">{{tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['email']\">{{(tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n <h5>{{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:tableField.option.currency :true:\r\n tableField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"form.propertySchema | vlPlaceholder: tableField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(tableField.label.style).style\"\r\n (click)=\"invokeGridBtnClick(tableField.option, tableitemInfo.propertySchema)\">\r\n {{ tableField.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- bottom pagination on grid -->\r\n<div\r\n *ngIf=\"tableitemInfo.data?.length && !tableitemInfo?.layoutSchema?.pagination?.top && tableitemInfo?.layoutSchema?.pagination?.bottom\">\r\n <vl-pagination [itemInfo]=\"tableitemInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n</div>\r\n", styles: [".grid-filter-row th{padding:8px;vertical-align:middle;background:#fafafa}.grid-filter-cell{min-width:150px}.grid-filter-input-wrapper{display:flex;align-items:stretch;width:100%}.grid-filter-input{width:100%;min-width:0;min-height:34px;padding:5px 8px;border:1px solid #d9d9d9;border-right:0;border-radius:4px 0 0 4px;outline:none}.grid-filter-input:focus{border-color:#1677ff}.grid-filter-operator-button,.grid-filter-clear-button{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:34px;padding:0;cursor:pointer;background:#fff;border:1px solid #d9d9d9}.grid-filter-operator-button{color:#595959;border-radius:0 4px 4px 0}.grid-filter-operator-button:hover,.grid-filter-operator-button--active{color:#1677ff;border-color:#1677ff}.grid-filter-clear-button{margin-left:5px;color:#ff4d4f;border-radius:4px}.grid-filter-clear-button:hover{color:#fff;background:#ff4d4f;border-color:#ff4d4f}.grid-filter-operator-menu{min-width:190px}:host ::ng-deep .ant-table-content.has-more-right{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 36px),transparent)}:host ::ng-deep .ant-table-content.has-more-left{-webkit-mask-image:linear-gradient(to left,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to left,#000 calc(100% - 36px),transparent)}:host ::ng-deep .ant-table-content.has-more-left.has-more-right{-webkit-mask-image:linear-gradient(to right,transparent,#000 36px,#000 calc(100% - 36px),transparent);mask-image:linear-gradient(to right,transparent,#000 36px,#000 calc(100% - 36px),transparent)}\n"] }]
5533
5838
  }], ctorParameters: () => [{ type: RequestService }, { type: UtilityService }, { type: NotificationService }, { type: i4$1.OktaAuthStateService }], propDecorators: { errorMessages: [{
5534
5839
  type: Input
5535
5840
  }], environment: [{
@@ -5703,7 +6008,7 @@ class HeaderMenuComponent {
5703
6008
  return items;
5704
6009
  }
5705
6010
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: HeaderMenuComponent, deps: [{ token: i4$1.OktaAuthStateService }, { token: RequestService }, { token: UtilityService }, { token: Couchdb }, { token: i1.Platform }], target: i0.ɵɵFactoryTarget.Component });
5706
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: HeaderMenuComponent, isStandalone: true, selector: "vl-header-menu", inputs: { headerItems: "headerItems", headerLayout: "headerLayout", environment: "environment", tenantName: "tenantName", projectSeleted: "projectSeleted", cartInfo: "cartInfo" }, outputs: { buttonClick: "buttonClick", showTableContent: "showTableContent", searchItems: "searchItems" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: a field without a placeholder in the project stays empty; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment stating that this container resolves by field NAME, not by field\r\n type. A correctly typed field with another name renders nothing, silently -->\r\n<header *ngIf=\"headerItems.length > 0\" [ngStyle]=\"getStyle(headerLayout?.style)\">\r\n\r\n <!-- @contract decision: resolves by field NAME, not by field type\r\n reality: the switch reads the LAST segment of scope.reference. Only logo, cart and\r\n searchInput render; any other name produces an empty element.\r\n failure: a correctly typed field with a different name renders nothing, silently.\r\n instead: name the field logo, cart or searchInput, or put it in another container -->\r\n <ng-container *ngFor=\"let header of headerItems\"\r\n [ngSwitch]=\"header.scope?.reference ? header.scope?.reference.split('/').pop() : 'default'\">\r\n\r\n <!-- Logo -->\r\n <ng-container *ngSwitchCase=\"'logo'\">\r\n <div *ngIf=\"isVisible(header)\" [ngStyle]=\"getStyle(header?.option?.style)\">\r\n\r\n <img [src]=\"header.option.src\" [alt]=\"header.alt || 'Logo'\"\r\n [ngStyle]=\"getStyle(header?.scope?.style)\" />\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Search -->\r\n <ng-container *ngSwitchCase=\"'searchInput'\">\r\n <div *ngIf=\"isVisible(header)\" [ngStyle]=\"getStyle(header?.option?.style)\">\r\n\r\n <input #search type=\"text\" [placeholder]=\"header.option.placeholder ?? ''\"\r\n (keyup.enter)=\"invokeSearchedItems(header.option, search)\"\r\n [ngStyle]=\"getStyle(header?.scope?.style)\" />\r\n\r\n <button *ngIf=\"header.option.button !== false\" type=\"button\"\r\n (click)=\"invokeSearchedItems(header.option, search)\" [ngStyle]=\"getStyle(header?.buttonStyle)\">\r\n <ng-container *ngIf=\"!header.caption; else searchTextContent\">\r\n <i nz-icon [nzType]=\"header.icon\" nzTheme=\"outline\"></i>\r\n </ng-container>\r\n\r\n <ng-template #searchTextContent>\r\n {{ header.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Cart -->\r\n <ng-container *ngSwitchCase=\"'cart'\">\r\n <div *ngIf=\"isVisible(header)\" [ngStyle]=\"getStyle(header?.option?.style)\"\r\n (click)=\"invokeShowtableContent(header.option)\">\r\n\r\n <span [ngStyle]=\"getStyle(header?.priceStyle)\">\r\n {{ cartInfo.totalPrice | currency : header.option?.currency ? header.option.currency : 'USD' }}\r\n </span>\r\n\r\n <span [ngStyle]=\"getStyle(header?.quantityStyle)\">\r\n {{ cartInfo.quantity }}\r\n </span>\r\n\r\n <i [class]=\"header.icon\" [ngStyle]=\"getStyle(header?.iconStyle)\">\r\n </i>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Default Button -->\r\n <ng-container *ngSwitchCase=\"'default'\">\r\n @if (header.option && header.option?.action && (header.option?.action == \"cart\")) {\r\n <div *ngIf=\"isVisible(header)\" [ngStyle]=\"getStyle(header?.option?.style)\"\r\n (click)=\"invokeShowtableContent(header.option)\">\r\n\r\n <span [ngStyle]=\"getStyle(header?.priceStyle)\">\r\n {{ cartInfo.totalPrice | currency : header.option?.currency ? header.option.currency : 'USD' }}\r\n </span>\r\n\r\n <span [ngStyle]=\"getStyle(header?.quantityStyle)\">\r\n {{ cartInfo.quantity }}\r\n </span>\r\n\r\n <i [class]=\"header.icon\" [ngStyle]=\"getStyle(header?.iconStyle)\">\r\n </i>\r\n </div>\r\n }@else {\r\n <div *ngIf=\"shouldShowDefaultButton(header)\" [ngStyle]=\"getStyle(header?.option?.style)\">\r\n\r\n <button type=\"button\" (click)=\"invokeHeaderButtonClick(header.option)\"\r\n [ngStyle]=\"getStyle(header?.buttonStyle)\">\r\n <ng-container *ngIf=\"!header.label; else buttonTextContent\">\r\n <i nz-icon [nzType]=\"header.icon\" nzTheme=\"outline\"></i>\r\n </ng-container>\r\n\r\n <ng-template #buttonTextContent>\r\n {{ header.label }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n }\r\n </ng-container>\r\n\r\n </ng-container>\r\n</header>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "pipe", type: i1$1.CurrencyPipe, name: "currency" }] });
6011
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: HeaderMenuComponent, isStandalone: true, selector: "vl-header-menu", inputs: { headerItems: "headerItems", headerLayout: "headerLayout", environment: "environment", tenantName: "tenantName", projectSeleted: "projectSeleted", cartInfo: "cartInfo" }, outputs: { buttonClick: "buttonClick", showTableContent: "showTableContent", searchItems: "searchItems" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: a field without a placeholder in the project stays empty; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment stating that this container resolves by field NAME, not by field\r\n type. A correctly typed field with another name renders nothing, silently -->\r\n<header *ngIf=\"headerItems.length > 0\" [ngStyle]=\"getStyle(headerLayout?.style)\">\r\n\r\n <!-- @contract decision: resolves by field NAME, not by field type\r\n reality: the switch reads the LAST segment of scope.reference. Only logo, cart and\r\n searchInput render; any other name produces an empty element.\r\n failure: a correctly typed field with a different name renders nothing, silently.\r\n instead: name the field logo, cart or searchInput, or put it in another container -->\r\n <ng-container *ngFor=\"let header of headerItems\"\r\n [ngSwitch]=\"header.scope?.reference ? header.scope?.reference.split('/').pop() : 'default'\">\r\n\r\n <!-- Logo -->\r\n <ng-container *ngSwitchCase=\"'logo'\">\r\n <div *ngIf=\"isVisible(header)\" [ngStyle]=\"getStyle(header?.option?.style)\">\r\n\r\n <img [src]=\"header.option.src\" [alt]=\"header.alt || 'Logo'\"\r\n [ngStyle]=\"getStyle(header?.scope?.style)\" />\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Search -->\r\n <ng-container *ngSwitchCase=\"'searchInput'\">\r\n <div *ngIf=\"isVisible(header)\" [ngStyle]=\"getStyle(header?.option?.style)\">\r\n\r\n <input #search type=\"text\" [placeholder]=\"header.option.placeholder ?? ''\"\r\n (keyup.enter)=\"invokeSearchedItems(header.option, search)\"\r\n [ngStyle]=\"getStyle(header?.scope?.style)\" />\r\n\r\n <button *ngIf=\"header.option.button !== false\" type=\"button\"\r\n (click)=\"invokeSearchedItems(header.option, search)\" [ngStyle]=\"getStyle(header?.buttonStyle)\">\r\n <ng-container *ngIf=\"!header.caption; else searchTextContent\">\r\n <i nz-icon [nzType]=\"header.icon\" nzTheme=\"outline\"></i>\r\n </ng-container>\r\n\r\n <ng-template #searchTextContent>\r\n {{ header.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Cart -->\r\n <ng-container *ngSwitchCase=\"'cart'\">\r\n <div *ngIf=\"isVisible(header)\" [ngStyle]=\"getStyle(header?.option?.style)\"\r\n (click)=\"invokeShowtableContent(header.option)\">\r\n\r\n <span [ngStyle]=\"getStyle(header?.priceStyle)\">\r\n {{ cartInfo.totalPrice | currency : header.option?.currency ? header.option.currency : 'USD' }}\r\n </span>\r\n\r\n <span [ngStyle]=\"getStyle(header?.quantityStyle)\">\r\n {{ cartInfo.quantity }}\r\n </span>\r\n\r\n <i [class]=\"header.icon\" [ngStyle]=\"getStyle(header?.iconStyle)\">\r\n </i>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Default Button -->\r\n <ng-container *ngSwitchCase=\"'default'\">\r\n @if (header.option && header.option?.action && (header.option?.action == \"cart\")) {\r\n <div *ngIf=\"isVisible(header)\" [ngStyle]=\"getStyle(header?.option?.style)\"\r\n (click)=\"invokeShowtableContent(header.option)\">\r\n\r\n <span [ngStyle]=\"getStyle(header?.priceStyle)\">\r\n {{ cartInfo.totalPrice | currency : header.option?.currency ? header.option.currency : 'USD' }}\r\n </span>\r\n\r\n <span [ngStyle]=\"getStyle(header?.quantityStyle)\">\r\n {{ cartInfo.quantity }}\r\n </span>\r\n\r\n <i [class]=\"header.icon\" [ngStyle]=\"getStyle(header?.iconStyle)\">\r\n </i>\r\n </div>\r\n }@else {\r\n <div *ngIf=\"shouldShowDefaultButton(header)\" [ngStyle]=\"getStyle(header?.option?.style)\">\r\n\r\n <button type=\"button\" (click)=\"invokeHeaderButtonClick(header.option)\"\r\n [ngStyle]=\"getStyle(header?.buttonStyle)\">\r\n <ng-container *ngIf=\"!header.label; else buttonTextContent\">\r\n <i nz-icon [nzType]=\"header.icon\" nzTheme=\"outline\"></i>\r\n </ng-container>\r\n\r\n <ng-template #buttonTextContent>\r\n {{ header.label }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n }\r\n </ng-container>\r\n\r\n </ng-container>\r\n</header>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }] });
5707
6012
  }
5708
6013
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: HeaderMenuComponent, decorators: [{
5709
6014
  type: Component,
@@ -5773,7 +6078,7 @@ class LogsComponent {
5773
6078
  this.expandedRow = this.expandedRow === index ? null : index;
5774
6079
  }
5775
6080
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: LogsComponent, deps: [{ token: NetworkLogService }], target: i0.ɵɵFactoryTarget.Component });
5776
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: LogsComponent, isStandalone: true, selector: "vl-logs", ngImport: i0, template: "<div class=\"network-log-wrapper\">\r\n\r\n <div class=\"network-log-table\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th>#</th>\r\n <th>Time</th>\r\n <th>Method</th>\r\n <th>URL</th>\r\n <th>Status</th>\r\n <th>Duration (ms)</th>\r\n <th>Size (KB)</th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n <ng-container *ngFor=\"let log of logs; let i = index\">\r\n\r\n <!-- MAIN ROW -->\r\n <tr class=\"log-row\" (click)=\"toggleRow(i)\">\r\n <td>{{ i + 1 }}</td>\r\n <td>{{ log.time }}</td>\r\n\r\n <td>\r\n <span class=\"method\" [class.get]=\"log.method === 'GET'\" [class.post]=\"log.method === 'POST'\"\r\n [class.put]=\"log.method === 'PUT'\" [class.delete]=\"log.method === 'DELETE'\">\r\n {{ log.method }}\r\n </span>\r\n </td>\r\n\r\n <td class=\"ellipsis\" [title]=\"log.url\">\r\n {{ log.url }}\r\n </td>\r\n\r\n <td [class.error]=\"log.status >= 400\">\r\n {{ log.status }}\r\n </td>\r\n\r\n <td>{{ log.duration }}</td>\r\n\r\n <td>{{ log.size | number:'1.0-2' }}</td>\r\n </tr>\r\n\r\n <!-- DETAILS ROW -->\r\n <tr *ngIf=\"expandedRow === i\" class=\"details-row\">\r\n <td colspan=\"7\">\r\n <div class=\"details-container\">\r\n\r\n <!-- META -->\r\n <div class=\"detail-block\">\r\n <h4>Meta</h4>\r\n <p><strong>Date:</strong> {{ log.date }}</p>\r\n <p><strong>Type:</strong> {{ log.type }}</p>\r\n </div>\r\n\r\n <!-- REQUEST -->\r\n <div class=\"detail-block\">\r\n <h4>Request</h4>\r\n <p><strong>Headers</strong></p>\r\n <pre>{{ log.requestHeaders | json }}</pre>\r\n\r\n <p><strong>Body</strong></p>\r\n <pre>{{ log.requestBody | json }}</pre>\r\n </div>\r\n\r\n <!-- RESPONSE -->\r\n <div class=\"detail-block\">\r\n <h4>Response</h4>\r\n <pre>{{ log.responseBody | json }}</pre>\r\n </div>\r\n\r\n <!-- PERFORMANCE -->\r\n <div class=\"detail-block\">\r\n <h4>Performance</h4>\r\n <p><strong>Duration:</strong> {{ log.duration }} ms</p>\r\n <p><strong>Size:</strong> {{ log.size | number:'1.0-2' }} KB</p>\r\n </div>\r\n\r\n </div>\r\n </td>\r\n </tr>\r\n\r\n </ng-container>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n</div>", styles: [".network-log-wrapper{padding:12px}.network-log-table{width:100%;overflow-x:auto}table{width:100%;border-collapse:collapse;font-size:13px;min-width:900px}thead th{background:#06b6d4;color:#fff;padding:8px;text-align:left;position:sticky;top:0;z-index:2}tbody td{padding:8px;border-bottom:1px solid #e5e7eb}.log-row{cursor:pointer}.log-row:hover{background:#f1f5f9}.details-row{background:#f8fafc}.method{padding:2px 8px;border-radius:4px;font-weight:600;font-size:11px;color:#fff}.method.get{background:#16a34a}.method.post{background:#2563eb}.method.put{background:#d97706}.method.delete{background:#dc2626}td.error{color:#dc2626;font-weight:700}.ellipsis{max-width:320px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.details-container{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}.detail-block{background:#fff;border:1px solid #e5e7eb;border-radius:6px;padding:10px}.detail-block h4{margin:0 0 6px;font-size:14px;color:#0f172a}pre{background:#020617;color:#e5e7eb;padding:8px;border-radius:6px;max-height:260px;overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-word}@media(max-width:768px){table{min-width:100%;font-size:12px}.ellipsis{max-width:140px}.details-container{grid-template-columns:1fr}pre{max-height:200px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.DecimalPipe, name: "number" }] });
6081
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: LogsComponent, isStandalone: true, selector: "vl-logs", ngImport: i0, template: "<div class=\"network-log-wrapper\">\r\n\r\n <div class=\"network-log-table\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th>#</th>\r\n <th>Time</th>\r\n <th>Method</th>\r\n <th>URL</th>\r\n <th>Status</th>\r\n <th>Duration (ms)</th>\r\n <th>Size (KB)</th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n <ng-container *ngFor=\"let log of logs; let i = index\">\r\n\r\n <!-- MAIN ROW -->\r\n <tr class=\"log-row\" (click)=\"toggleRow(i)\">\r\n <td>{{ i + 1 }}</td>\r\n <td>{{ log.time }}</td>\r\n\r\n <td>\r\n <span class=\"method\" [class.get]=\"log.method === 'GET'\" [class.post]=\"log.method === 'POST'\"\r\n [class.put]=\"log.method === 'PUT'\" [class.delete]=\"log.method === 'DELETE'\">\r\n {{ log.method }}\r\n </span>\r\n </td>\r\n\r\n <td class=\"ellipsis\" [title]=\"log.url\">\r\n {{ log.url }}\r\n </td>\r\n\r\n <td [class.error]=\"log.status >= 400\">\r\n {{ log.status }}\r\n </td>\r\n\r\n <td>{{ log.duration }}</td>\r\n\r\n <td>{{ log.size | number:'1.0-2' }}</td>\r\n </tr>\r\n\r\n <!-- DETAILS ROW -->\r\n <tr *ngIf=\"expandedRow === i\" class=\"details-row\">\r\n <td colspan=\"7\">\r\n <div class=\"details-container\">\r\n\r\n <!-- META -->\r\n <div class=\"detail-block\">\r\n <h4>Meta</h4>\r\n <p><strong>Date:</strong> {{ log.date }}</p>\r\n <p><strong>Type:</strong> {{ log.type }}</p>\r\n </div>\r\n\r\n <!-- REQUEST -->\r\n <div class=\"detail-block\">\r\n <h4>Request</h4>\r\n <p><strong>Headers</strong></p>\r\n <pre>{{ log.requestHeaders | json }}</pre>\r\n\r\n <p><strong>Body</strong></p>\r\n <pre>{{ log.requestBody | json }}</pre>\r\n </div>\r\n\r\n <!-- RESPONSE -->\r\n <div class=\"detail-block\">\r\n <h4>Response</h4>\r\n <pre>{{ log.responseBody | json }}</pre>\r\n </div>\r\n\r\n <!-- PERFORMANCE -->\r\n <div class=\"detail-block\">\r\n <h4>Performance</h4>\r\n <p><strong>Duration:</strong> {{ log.duration }} ms</p>\r\n <p><strong>Size:</strong> {{ log.size | number:'1.0-2' }} KB</p>\r\n </div>\r\n\r\n </div>\r\n </td>\r\n </tr>\r\n\r\n </ng-container>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n</div>", styles: [".network-log-wrapper{padding:12px}.network-log-table{width:100%;overflow-x:auto}table{width:100%;border-collapse:collapse;font-size:13px;min-width:900px}thead th{background:#06b6d4;color:#fff;padding:8px;text-align:left;position:sticky;top:0;z-index:2}tbody td{padding:8px;border-bottom:1px solid #e5e7eb}.log-row{cursor:pointer}.log-row:hover{background:#f1f5f9}.details-row{background:#f8fafc}.method{padding:2px 8px;border-radius:4px;font-weight:600;font-size:11px;color:#fff}.method.get{background:#16a34a}.method.post{background:#2563eb}.method.put{background:#d97706}.method.delete{background:#dc2626}td.error{color:#dc2626;font-weight:700}.ellipsis{max-width:320px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.details-container{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}.detail-block{background:#fff;border:1px solid #e5e7eb;border-radius:6px;padding:10px}.detail-block h4{margin:0 0 6px;font-size:14px;color:#0f172a}pre{background:#020617;color:#e5e7eb;padding:8px;border-radius:6px;max-height:260px;overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-word}@media(max-width:768px){table{min-width:100%;font-size:12px}.ellipsis{max-width:140px}.details-container{grid-template-columns:1fr}pre{max-height:200px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i5.JsonPipe, name: "json" }, { kind: "pipe", type: i5.DecimalPipe, name: "number" }] });
5777
6082
  }
5778
6083
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: LogsComponent, decorators: [{
5779
6084
  type: Component,
@@ -5782,774 +6087,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
5782
6087
  ], selector: 'vl-logs', template: "<div class=\"network-log-wrapper\">\r\n\r\n <div class=\"network-log-table\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th>#</th>\r\n <th>Time</th>\r\n <th>Method</th>\r\n <th>URL</th>\r\n <th>Status</th>\r\n <th>Duration (ms)</th>\r\n <th>Size (KB)</th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n <ng-container *ngFor=\"let log of logs; let i = index\">\r\n\r\n <!-- MAIN ROW -->\r\n <tr class=\"log-row\" (click)=\"toggleRow(i)\">\r\n <td>{{ i + 1 }}</td>\r\n <td>{{ log.time }}</td>\r\n\r\n <td>\r\n <span class=\"method\" [class.get]=\"log.method === 'GET'\" [class.post]=\"log.method === 'POST'\"\r\n [class.put]=\"log.method === 'PUT'\" [class.delete]=\"log.method === 'DELETE'\">\r\n {{ log.method }}\r\n </span>\r\n </td>\r\n\r\n <td class=\"ellipsis\" [title]=\"log.url\">\r\n {{ log.url }}\r\n </td>\r\n\r\n <td [class.error]=\"log.status >= 400\">\r\n {{ log.status }}\r\n </td>\r\n\r\n <td>{{ log.duration }}</td>\r\n\r\n <td>{{ log.size | number:'1.0-2' }}</td>\r\n </tr>\r\n\r\n <!-- DETAILS ROW -->\r\n <tr *ngIf=\"expandedRow === i\" class=\"details-row\">\r\n <td colspan=\"7\">\r\n <div class=\"details-container\">\r\n\r\n <!-- META -->\r\n <div class=\"detail-block\">\r\n <h4>Meta</h4>\r\n <p><strong>Date:</strong> {{ log.date }}</p>\r\n <p><strong>Type:</strong> {{ log.type }}</p>\r\n </div>\r\n\r\n <!-- REQUEST -->\r\n <div class=\"detail-block\">\r\n <h4>Request</h4>\r\n <p><strong>Headers</strong></p>\r\n <pre>{{ log.requestHeaders | json }}</pre>\r\n\r\n <p><strong>Body</strong></p>\r\n <pre>{{ log.requestBody | json }}</pre>\r\n </div>\r\n\r\n <!-- RESPONSE -->\r\n <div class=\"detail-block\">\r\n <h4>Response</h4>\r\n <pre>{{ log.responseBody | json }}</pre>\r\n </div>\r\n\r\n <!-- PERFORMANCE -->\r\n <div class=\"detail-block\">\r\n <h4>Performance</h4>\r\n <p><strong>Duration:</strong> {{ log.duration }} ms</p>\r\n <p><strong>Size:</strong> {{ log.size | number:'1.0-2' }} KB</p>\r\n </div>\r\n\r\n </div>\r\n </td>\r\n </tr>\r\n\r\n </ng-container>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n</div>", styles: [".network-log-wrapper{padding:12px}.network-log-table{width:100%;overflow-x:auto}table{width:100%;border-collapse:collapse;font-size:13px;min-width:900px}thead th{background:#06b6d4;color:#fff;padding:8px;text-align:left;position:sticky;top:0;z-index:2}tbody td{padding:8px;border-bottom:1px solid #e5e7eb}.log-row{cursor:pointer}.log-row:hover{background:#f1f5f9}.details-row{background:#f8fafc}.method{padding:2px 8px;border-radius:4px;font-weight:600;font-size:11px;color:#fff}.method.get{background:#16a34a}.method.post{background:#2563eb}.method.put{background:#d97706}.method.delete{background:#dc2626}td.error{color:#dc2626;font-weight:700}.ellipsis{max-width:320px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.details-container{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}.detail-block{background:#fff;border:1px solid #e5e7eb;border-radius:6px;padding:10px}.detail-block h4{margin:0 0 6px;font-size:14px;color:#0f172a}pre{background:#020617;color:#e5e7eb;padding:8px;border-radius:6px;max-height:260px;overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-word}@media(max-width:768px){table{min-width:100%;font-size:12px}.ellipsis{max-width:140px}.details-container{grid-template-columns:1fr}pre{max-height:200px}}\n"] }]
5783
6088
  }], ctorParameters: () => [{ type: NetworkLogService }] });
5784
6089
 
5785
- const CONTROL_MAP = {
5786
- string: ['textInput', 'textArea', 'password', 'combobox', 'autocomplete', 'checkbox', 'switch', 'radiobutton', 'dropdown', 'email'],
5787
- number: ['textInput', 'spinner', 'slider'], integer: ['textInput', 'spinner', 'slider'],
5788
- bigdecimal: ['textInput', 'spinner'], boolean: ['checkbox', 'switch', 'radiobutton'],
5789
- date: ['date'], time: ['time'], datetime: ['datetime'], media: ['media']
5790
- };
5791
- function basicType(property) {
5792
- const value = `${property?.type || 'string'}`.toLowerCase();
5793
- return { image: 'media', checkbox: 'boolean', datetime: 'datetime', 'date-time': 'datetime', 'date&time': 'datetime', decimal: 'bigdecimal' }[value] || value;
5794
- }
5795
- function controlFor(property, option) {
5796
- const type = basicType(property);
5797
- if (['date', 'time', 'datetime', 'media'].includes(type))
5798
- return type;
5799
- const explicit = option?.control;
5800
- const control = { textfield: 'textInput', textarea: 'textArea', radioButton: 'radiobutton' }[explicit] || explicit;
5801
- return control || (type === 'boolean' ? 'checkbox' : 'textInput');
5802
- }
5803
- function supported(property, option) {
5804
- const choices = CONTROL_MAP[basicType(property)];
5805
- return !!choices?.includes(controlFor(property, option));
5806
- }
5807
- function rulesFor(property, option) {
5808
- const result = { ...(option?.fieldData?.validation || {}), ...(option?.validation || {}), ...(property?.validation || {}) };
5809
- for (const key of ['minLength', 'maxLength', 'pattern', 'minimum', 'maximum', 'multipleOf', 'enum']) {
5810
- if (property?.[key] !== undefined)
5811
- result[key] = property[key];
5812
- }
5813
- if (result.minLength === undefined)
5814
- result.minLength = property?.minlength;
5815
- if (result.maxLength === undefined)
5816
- result.maxLength = property?.maxlength;
5817
- if (result.minimum === undefined)
5818
- result.minimum = property?.min;
5819
- if (result.maximum === undefined)
5820
- result.maximum = property?.max;
5821
- return result;
5822
- }
5823
- function keyboardFor(property, option) {
5824
- if (['native', 'none'].includes(option?.keyboardType))
5825
- return null;
5826
- if (['email', 'numeric', 'alpha', 'string', 'date', 'time'].includes(option?.keyboardType))
5827
- return option.keyboardType;
5828
- // Layout-selected keyboard wins. Omitted/unknown keyboards use the project's string default.
5829
- return 'string';
5830
- }
5831
- function classes(style) {
5832
- return Array.isArray(style?.classes) ? style.classes.filter(Boolean).join(' ') : style?.classes || '';
5833
- }
5834
- function styles(style) {
5835
- const { classes: ignored, ...rest } = style || {};
5836
- return rest;
5837
- }
5838
- function flagValues(property, option) {
5839
- if (basicType(property) === 'boolean') {
5840
- const on = option?.checkedBooleanValue !== false;
5841
- return { on, off: !on };
5842
- }
5843
- const values = rulesFor(property, option).enum;
5844
- return { on: option?.checkedValue ?? values?.[0] ?? '1', off: option?.uncheckedValue ?? values?.[1] ?? '0' };
5845
- }
5846
- function choicesFor(property, option, data) {
5847
- const rules = rulesFor(property, option);
5848
- const source = Array.isArray(data) ? data : option?.values ?? rules.enum ?? property?.data ?? [];
5849
- if (!Array.isArray(source))
5850
- return [];
5851
- const valueKey = option?.optionValue || option?.optionvalue || option?.autocomplete?.response?.value;
5852
- const labelKey = option?.optionLabel || option?.optionlabel || option?.autocomplete?.response?.label;
5853
- return source.map((entry, index) => {
5854
- const object = entry !== null && typeof entry === 'object';
5855
- const value = object && valueKey ? entry[valueKey] : entry;
5856
- const override = Array.isArray(option?.labels) ? option.labels[index] : option?.labels?.[value];
5857
- const label = override ?? (object && labelKey ? entry[labelKey] : object ? entry.label ?? entry.caption ?? entry.value ?? '' : entry);
5858
- return { value, label: `${label ?? ''}`, original: entry };
5859
- });
5860
- }
5861
- /** Decimal arithmetic uses integer coefficients; no conversion through Number. */
5862
- function decimalParts(value) {
5863
- if (typeof value !== 'string' && typeof value !== 'number')
5864
- return null;
5865
- const match = /^([+-]?)(\d*)(?:\.(\d*))?(?:e([+-]?\d+))?$/i.exec(`${value}`.trim());
5866
- if (!match || !(match[2] || match[3]))
5867
- return null;
5868
- const exponent = Number(match[4] || 0);
5869
- if (!Number.isInteger(exponent) || Math.abs(exponent) > 10000)
5870
- return null;
5871
- let scale = (match[3]?.length || 0) - exponent;
5872
- let coefficient = BigInt((match[1] === '-' ? '-' : '') + ((match[2] || '') + (match[3] || '') || '0'));
5873
- if (scale < 0) {
5874
- coefficient *= 10n ** BigInt(-scale);
5875
- scale = 0;
5876
- }
5877
- return { coefficient, scale };
5878
- }
5879
- function decimalString(coefficient, scale) {
5880
- const negative = coefficient < 0n, digits = (negative ? -coefficient : coefficient).toString().padStart(scale + 1, '0');
5881
- const number = scale ? digits.slice(0, -scale) + '.' + digits.slice(-scale) : digits;
5882
- return (negative ? '-' : '') + number;
5883
- }
5884
- function decimalCompare(a, b) {
5885
- const left = decimalParts(a), right = decimalParts(b);
5886
- if (!left || !right)
5887
- return null;
5888
- const scale = Math.max(left.scale, right.scale);
5889
- const diff = left.coefficient * 10n ** BigInt(scale - left.scale) - right.coefficient * 10n ** BigInt(scale - right.scale);
5890
- return diff < 0n ? -1 : diff > 0n ? 1 : 0;
5891
- }
5892
- function decimalStep(value, step, direction) {
5893
- const a = decimalParts(value === '' || value == null ? '0' : value), b = decimalParts(step ?? '1');
5894
- if (!a || !b || b.coefficient <= 0n)
5895
- return null;
5896
- const scale = Math.max(a.scale, b.scale);
5897
- return decimalString(a.coefficient * 10n ** BigInt(scale - a.scale) + BigInt(direction) * b.coefficient * 10n ** BigInt(scale - b.scale), scale);
5898
- }
5899
- function validateValue(value, property, option) {
5900
- if (!supported(property, option))
5901
- return { unsupportedControl: true };
5902
- const type = basicType(property), control = controlFor(property, option), rules = rulesFor(property, option);
5903
- const empty = value === null || value === undefined || value === '';
5904
- const required = option?.required ?? property?.required ?? false;
5905
- if (required && (empty || (['checkbox', 'switch'].includes(control) && value !== flagValues(property, option).on)))
5906
- return { required: true };
5907
- if (empty)
5908
- return null;
5909
- const errors = {};
5910
- if (type === 'boolean' && typeof value !== 'boolean')
5911
- errors.boolean = true;
5912
- if (['number', 'integer', 'bigdecimal'].includes(type)) {
5913
- const parts = decimalParts(value);
5914
- if (!parts || (type !== 'bigdecimal' && !Number.isFinite(Number(value))))
5915
- errors.number = true;
5916
- else {
5917
- if (type === 'integer' && (parts.coefficient % 10n ** BigInt(parts.scale) !== 0n || !Number.isSafeInteger(Number(value))))
5918
- errors.integer = true;
5919
- if (rules.minimum !== undefined && decimalCompare(value, rules.minimum) === -1)
5920
- errors.minimum = { min: rules.minimum };
5921
- if (rules.maximum !== undefined && decimalCompare(value, rules.maximum) === 1)
5922
- errors.maximum = { max: rules.maximum };
5923
- if (rules.multipleOf !== undefined) {
5924
- const multiple = decimalParts(rules.multipleOf);
5925
- if (multiple && multiple.coefficient > 0n) {
5926
- const scale = Math.max(parts.scale, multiple.scale);
5927
- if ((parts.coefficient * 10n ** BigInt(scale - parts.scale)) % (multiple.coefficient * 10n ** BigInt(scale - multiple.scale)) !== 0n)
5928
- errors.multipleOf = true;
5929
- }
5930
- else
5931
- errors.configuration = 'multipleOf must be positive';
5932
- }
5933
- if (option?.decimals !== undefined && parts.scale > Number(option.decimals))
5934
- errors.decimals = true;
5935
- }
5936
- }
5937
- if (type === 'string' && typeof value === 'string') {
5938
- if (rules.minLength !== undefined && value.length < rules.minLength)
5939
- errors.minlength = { requiredLength: rules.minLength, actualLength: value.length };
5940
- if (rules.maxLength !== undefined && value.length > rules.maxLength)
5941
- errors.maxlength = { requiredLength: rules.maxLength, actualLength: value.length };
5942
- if (rules.pattern) {
5943
- try {
5944
- if (!new RegExp(`^(?:${rules.pattern})$`).test(value))
5945
- errors.pattern = true;
5946
- }
5947
- catch {
5948
- errors.configuration = 'Invalid validation pattern';
5949
- }
5950
- }
5951
- if ((control === 'email' || option?.email || property?.format === 'email') && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
5952
- errors.email = true;
5953
- }
5954
- if (rules.enum && !(control === 'combobox' && option?.allowCustom === true) && !rules.enum.some((v) => Object.is(v, value)))
5955
- errors.enum = true;
5956
- if (['date', 'time', 'datetime'].includes(type)) {
5957
- const date = value instanceof Date ? value : new Date(type === 'time' && /^\d\d:\d\d/.test(`${value}`) ? `2000-01-01T${value}` : value);
5958
- if (Number.isNaN(date.getTime()))
5959
- errors.date = true;
5960
- for (const [key, compare] of [['minDate', -1], ['maxDate', 1]]) {
5961
- if (option?.[key]) {
5962
- const boundary = new Date(option[key]);
5963
- if ((compare === -1 && date < boundary) || (compare === 1 && date > boundary))
5964
- errors[key] = true;
5965
- }
5966
- }
5967
- }
5968
- return Object.keys(errors).length ? errors : null;
5969
- }
5970
-
5971
- class ControlBase {
5972
- value;
5973
- option = {};
5974
- property = {};
5975
- control = '';
5976
- inputId = '';
5977
- inputStyle = {};
5978
- disabled = false;
5979
- readonly = false;
5980
- keyboard = null;
5981
- changed = new EventEmitter();
5982
- blurred = new EventEmitter();
5983
- itemSelected = new EventEmitter();
5984
- classes = classes;
5985
- styles = styles;
5986
- get placeholder() { return this.option.placeholder ?? this.property.placeholder ?? ''; }
5987
- get inputMode() { return this.keyboard === 'numeric' ? 'decimal' : this.keyboard === 'email' ? 'email' : 'text'; }
5988
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ControlBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
5989
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.2", type: ControlBase, isStandalone: true, inputs: { value: "value", option: "option", property: "property", control: "control", inputId: "inputId", inputStyle: "inputStyle", disabled: "disabled", readonly: "readonly", keyboard: "keyboard" }, outputs: { changed: "changed", blurred: "blurred", itemSelected: "itemSelected" }, ngImport: i0 });
5990
- }
5991
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ControlBase, decorators: [{
5992
- type: Directive
5993
- }], propDecorators: { value: [{
5994
- type: Input
5995
- }], option: [{
5996
- type: Input
5997
- }], property: [{
5998
- type: Input
5999
- }], control: [{
6000
- type: Input
6001
- }], inputId: [{
6002
- type: Input
6003
- }], inputStyle: [{
6004
- type: Input
6005
- }], disabled: [{
6006
- type: Input
6007
- }], readonly: [{
6008
- type: Input
6009
- }], keyboard: [{
6010
- type: Input
6011
- }], changed: [{
6012
- type: Output
6013
- }], blurred: [{
6014
- type: Output
6015
- }], itemSelected: [{
6016
- type: Output
6017
- }] } });
6018
-
6019
- class TextControlComponent extends ControlBase {
6020
- reveal = false;
6021
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: TextControlComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
6022
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: TextControlComponent, isStandalone: true, selector: "vl-text-control", usesInheritance: true, ngImport: i0, template: `
6023
- <textarea pTextarea *ngIf="control === 'textArea'; else single" [id]="inputId" [ngModel]="value"
6024
- (ngModelChange)="changed.emit($event)" [ngModelOptions]="{standalone:true}" (blur)="blurred.emit()"
6025
- [disabled]="disabled" [readOnly]="readonly" [placeholder]="placeholder" [rows]="option.rows ?? 3"
6026
- [attr.inputmode]="inputMode" [attr.autocomplete]="option.autocompleteHint || 'off'"
6027
- [vlCustomKeyboard]="keyboard" [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)"></textarea>
6028
- <ng-template #single><div class="text-wrap" [class.text-wrap--password]="control === 'password' && option.showPasswordToggle !== false">
6029
- <input pInputText [id]="inputId" [type]="control === 'password' && !reveal ? 'password' : control === 'email' ? 'email' : 'text'"
6030
- [ngModel]="value" (ngModelChange)="changed.emit($event)" [ngModelOptions]="{standalone:true}" (blur)="blurred.emit()"
6031
- [disabled]="disabled" [readOnly]="readonly" [placeholder]="placeholder" [attr.inputmode]="inputMode"
6032
- [attr.autocomplete]="option.autocompleteHint || (control === 'password' ? 'current-password' : 'off')"
6033
- [vlCustomKeyboard]="keyboard" [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)" />
6034
- <button *ngIf="control === 'password' && option.showPasswordToggle !== false" type="button"
6035
- [disabled]="disabled" (click)="reveal=!reveal" [attr.aria-label]="reveal?'Hide password':'Show password'"
6036
- [attr.aria-pressed]="reveal">{{reveal?'Hide':'Show'}}</button>
6037
- </div></ng-template>`, isInline: true, styles: [":host{display:block;min-width:0}.text-wrap{display:contents}.text-wrap--password{display:flex;align-items:center;gap:6px}.text-wrap--password input{min-width:0;flex:1}button{font:inherit;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i4$2.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }] });
6038
- }
6039
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: TextControlComponent, decorators: [{
6040
- type: Component,
6041
- args: [{ selector: 'vl-text-control', standalone: true, imports: [CommonModule, FormsModule, CustomKeyboardDirective, InputTextModule, TextareaModule], template: `
6042
- <textarea pTextarea *ngIf="control === 'textArea'; else single" [id]="inputId" [ngModel]="value"
6043
- (ngModelChange)="changed.emit($event)" [ngModelOptions]="{standalone:true}" (blur)="blurred.emit()"
6044
- [disabled]="disabled" [readOnly]="readonly" [placeholder]="placeholder" [rows]="option.rows ?? 3"
6045
- [attr.inputmode]="inputMode" [attr.autocomplete]="option.autocompleteHint || 'off'"
6046
- [vlCustomKeyboard]="keyboard" [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)"></textarea>
6047
- <ng-template #single><div class="text-wrap" [class.text-wrap--password]="control === 'password' && option.showPasswordToggle !== false">
6048
- <input pInputText [id]="inputId" [type]="control === 'password' && !reveal ? 'password' : control === 'email' ? 'email' : 'text'"
6049
- [ngModel]="value" (ngModelChange)="changed.emit($event)" [ngModelOptions]="{standalone:true}" (blur)="blurred.emit()"
6050
- [disabled]="disabled" [readOnly]="readonly" [placeholder]="placeholder" [attr.inputmode]="inputMode"
6051
- [attr.autocomplete]="option.autocompleteHint || (control === 'password' ? 'current-password' : 'off')"
6052
- [vlCustomKeyboard]="keyboard" [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)" />
6053
- <button *ngIf="control === 'password' && option.showPasswordToggle !== false" type="button"
6054
- [disabled]="disabled" (click)="reveal=!reveal" [attr.aria-label]="reveal?'Hide password':'Show password'"
6055
- [attr.aria-pressed]="reveal">{{reveal?'Hide':'Show'}}</button>
6056
- </div></ng-template>`, styles: [":host{display:block;min-width:0}.text-wrap{display:contents}.text-wrap--password{display:flex;align-items:center;gap:6px}.text-wrap--password input{min-width:0;flex:1}button{font:inherit;cursor:pointer}\n"] }]
6057
- }] });
6058
-
6059
- class NumericControlComponent extends ControlBase {
6060
- focused = false;
6061
- editingValue = null;
6062
- focusInput() { this.editingValue = this.value == null ? '' : `${this.value}`; this.focused = true; }
6063
- get integer() { return basicType(this.property) === 'integer'; }
6064
- get exact() { return basicType(this.property) === 'bigdecimal'; }
6065
- get rules() { return rulesFor(this.property, this.option); }
6066
- get displayValue() {
6067
- if (this.focused)
6068
- return this.editingValue ?? this.value ?? '';
6069
- if (this.control === 'slider' || this.value == null || this.value === '')
6070
- return this.value ?? '';
6071
- if (!this.option.numberStyle && this.option.grouping === undefined)
6072
- return this.value;
6073
- if (!this.exact && typeof this.value === 'number') {
6074
- try {
6075
- return new Intl.NumberFormat(this.option.locale, {
6076
- useGrouping: this.option.grouping ?? true,
6077
- maximumFractionDigits: this.option.decimals ?? 20,
6078
- ...(this.option.numberStyle === 'currency' && this.option.currency ? { style: 'currency', currency: this.option.currency } : {})
6079
- }).format(this.value);
6080
- }
6081
- catch {
6082
- return this.value;
6083
- }
6084
- }
6085
- // BigDecimal is never converted to a JS number for display.
6086
- const parts = `${this.value}`.split('.');
6087
- return (this.option.grouping ? parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') : parts[0]) + (parts.length > 1 ? '.' + parts[1] : '');
6088
- }
6089
- edit(raw) {
6090
- if (this.readonly || this.disabled)
6091
- return;
6092
- this.editingValue = raw;
6093
- if (raw === '') {
6094
- this.changed.emit(null);
6095
- return;
6096
- }
6097
- this.changed.emit(this.exact || !decimalParts(raw) || (this.integer && !Number.isSafeInteger(Number(raw))) ? raw : Number(raw));
6098
- }
6099
- step(direction) {
6100
- if (this.readonly || this.disabled)
6101
- return;
6102
- let next = decimalStep(this.value, this.option.step ?? '1', direction);
6103
- if (next === null)
6104
- return;
6105
- if (this.rules.minimum !== undefined && decimalCompare(next, this.rules.minimum) === -1)
6106
- next = `${this.rules.minimum}`;
6107
- if (this.rules.maximum !== undefined && decimalCompare(next, this.rules.maximum) === 1)
6108
- next = `${this.rules.maximum}`;
6109
- this.changed.emit(this.exact ? next : Number(next));
6110
- this.blurred.emit();
6111
- }
6112
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NumericControlComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
6113
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: NumericControlComponent, isStandalone: true, selector: "vl-numeric-control", usesInheritance: true, ngImport: i0, template: `
6114
- <div class="numeric-wrap" [class.numeric-wrap--group]="control === 'spinner' || (control === 'slider' && option.showValue) || option.unitLabel || option.unit">
6115
- <button *ngIf="control==='spinner'" type="button" aria-label="Decrease value" [disabled]="disabled||readonly" (click)="step(-1)">−</button>
6116
- <input pInputText [id]="inputId" [type]="control==='slider'?'range':'text'" [value]="displayValue" [disabled]="disabled || (readonly && control==='slider')"
6117
- [readOnly]="readonly" [attr.inputmode]="keyboard==='numeric'?'decimal':inputMode" [placeholder]="placeholder"
6118
- [attr.min]="rules.minimum ?? (control==='slider'?0:null)" [attr.max]="rules.maximum ?? (control==='slider'?100:null)"
6119
- [attr.step]="option.step ?? (integer?1:'any')" [vlCustomKeyboard]="control==='slider'?null:keyboard"
6120
- (input)="edit($any($event.target).value)" (focus)="focusInput()" (blur)="focused=false; editingValue=null; blurred.emit()"
6121
- [attr.aria-readonly]="readonly" [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)" />
6122
- <button *ngIf="control==='spinner'" type="button" aria-label="Increase value" [disabled]="disabled||readonly" (click)="step(1)">+</button>
6123
- <span *ngIf="control==='slider'&&option.showValue">{{value}}</span>
6124
- <span *ngIf="option.unitLabel || option.unit">{{option.unitLabel || option.unit}}</span>
6125
- </div>`, isInline: true, styles: [":host{display:block;min-width:0}.numeric-wrap{display:contents}.numeric-wrap--group{display:flex;align-items:center;gap:6px}.numeric-wrap--group input{min-width:0;flex:1}button{font:inherit;padding:7px 12px;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }] });
6126
- }
6127
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NumericControlComponent, decorators: [{
6128
- type: Component,
6129
- args: [{ selector: 'vl-numeric-control', standalone: true, imports: [CommonModule, CustomKeyboardDirective, InputTextModule], template: `
6130
- <div class="numeric-wrap" [class.numeric-wrap--group]="control === 'spinner' || (control === 'slider' && option.showValue) || option.unitLabel || option.unit">
6131
- <button *ngIf="control==='spinner'" type="button" aria-label="Decrease value" [disabled]="disabled||readonly" (click)="step(-1)">−</button>
6132
- <input pInputText [id]="inputId" [type]="control==='slider'?'range':'text'" [value]="displayValue" [disabled]="disabled || (readonly && control==='slider')"
6133
- [readOnly]="readonly" [attr.inputmode]="keyboard==='numeric'?'decimal':inputMode" [placeholder]="placeholder"
6134
- [attr.min]="rules.minimum ?? (control==='slider'?0:null)" [attr.max]="rules.maximum ?? (control==='slider'?100:null)"
6135
- [attr.step]="option.step ?? (integer?1:'any')" [vlCustomKeyboard]="control==='slider'?null:keyboard"
6136
- (input)="edit($any($event.target).value)" (focus)="focusInput()" (blur)="focused=false; editingValue=null; blurred.emit()"
6137
- [attr.aria-readonly]="readonly" [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)" />
6138
- <button *ngIf="control==='spinner'" type="button" aria-label="Increase value" [disabled]="disabled||readonly" (click)="step(1)">+</button>
6139
- <span *ngIf="control==='slider'&&option.showValue">{{value}}</span>
6140
- <span *ngIf="option.unitLabel || option.unit">{{option.unitLabel || option.unit}}</span>
6141
- </div>`, styles: [":host{display:block;min-width:0}.numeric-wrap{display:contents}.numeric-wrap--group{display:flex;align-items:center;gap:6px}.numeric-wrap--group input{min-width:0;flex:1}button{font:inherit;padding:7px 12px;cursor:pointer}\n"] }]
6142
- }] });
6143
-
6144
- class ToggleControlComponent extends ControlBase {
6145
- get checked() { return Object.is(this.value, flagValues(this.property, this.option).on); }
6146
- toggle(checked) { if (!this.disabled && !this.readonly)
6147
- this.changed.emit(checked ? flagValues(this.property, this.option).on : flagValues(this.property, this.option).off); }
6148
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ToggleControlComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
6149
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: ToggleControlComponent, isStandalone: true, selector: "vl-toggle-control", usesInheritance: true, ngImport: i0, template: `
6150
- <span class="toggle-wrap"><input [id]="inputId" type="checkbox" [class.toggle]="control==='switch'"
6151
- [attr.role]="control==='switch'?'switch':null" [checked]="checked" [disabled]="disabled||readonly"
6152
- (change)="toggle($any($event.target).checked)" (blur)="blurred.emit()" [ngStyle]="styles(inputStyle)" />
6153
- <span *ngIf="control==='switch'">{{checked?option.labelOn:option.labelOff}}</span></span>`, isInline: true, styles: [".toggle-wrap{display:inline-flex;gap:8px;align-items:center}.toggle{appearance:none;position:relative;width:36px;height:22px;border-radius:16px;background:#94a3b8;cursor:pointer}.toggle:before{content:\"\";position:absolute;width:16px;height:16px;top:3px;left:3px;border-radius:50%;background:#fff}.toggle:checked{background:#2563eb}.toggle:checked:before{transform:translate(14px)}input:focus-visible{outline:2px solid #2563eb;outline-offset:3px}input:disabled{opacity:.55;cursor:default}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
6154
- }
6155
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ToggleControlComponent, decorators: [{
6156
- type: Component,
6157
- args: [{ selector: 'vl-toggle-control', standalone: true, imports: [CommonModule], template: `
6158
- <span class="toggle-wrap"><input [id]="inputId" type="checkbox" [class.toggle]="control==='switch'"
6159
- [attr.role]="control==='switch'?'switch':null" [checked]="checked" [disabled]="disabled||readonly"
6160
- (change)="toggle($any($event.target).checked)" (blur)="blurred.emit()" [ngStyle]="styles(inputStyle)" />
6161
- <span *ngIf="control==='switch'">{{checked?option.labelOn:option.labelOff}}</span></span>`, styles: [".toggle-wrap{display:inline-flex;gap:8px;align-items:center}.toggle{appearance:none;position:relative;width:36px;height:22px;border-radius:16px;background:#94a3b8;cursor:pointer}.toggle:before{content:\"\";position:absolute;width:16px;height:16px;top:3px;left:3px;border-radius:50%;background:#fff}.toggle:checked{background:#2563eb}.toggle:checked:before{transform:translate(14px)}input:focus-visible{outline:2px solid #2563eb;outline-offset:3px}input:disabled{opacity:.55;cursor:default}\n"] }]
6162
- }] });
6163
-
6164
- class SelectionControlComponent extends ControlBase {
6165
- items;
6166
- suggestions = [];
6167
- equal = Object.is;
6168
- choices = [];
6169
- get display() { return this.choices.find(item => Object.is(item.value, this.value)) ?? this.value ?? ''; }
6170
- ngOnChanges() {
6171
- const next = basicType(this.property) === 'boolean' && this.items === undefined && !this.option.values
6172
- ? choicesFor(this.property, { ...this.option, values: [true, false], labels: this.option.labels ?? ['Yes', 'No'] })
6173
- : choicesFor(this.property, this.option, this.items);
6174
- // Keep PrimeNG's selected object stable across value/option change detection.
6175
- // Each component owns its suggestions; typing in one cannot replace another's list.
6176
- if (next.length !== this.choices.length || next.some((item, index) => !Object.is(item.value, this.choices[index]?.value) || item.label !== this.choices[index]?.label || item.original !== this.choices[index]?.original)) {
6177
- this.choices = next;
6178
- this.suggestions = next;
6179
- }
6180
- }
6181
- complete(event) { const query = `${event.query ?? ''}`.toLowerCase(); this.suggestions = this.choices.filter(item => this.option.filterMode === 'contains' ? item.label.toLowerCase().includes(query) : item.label.toLowerCase().startsWith(query)); }
6182
- typed(value) {
6183
- if (typeof value === 'string' && (this.control === 'autocomplete' || this.option.allowCustom === true) && !this.disabled && !this.readonly)
6184
- this.changed.emit(value);
6185
- if (value === null && !this.disabled && !this.readonly)
6186
- this.changed.emit(null);
6187
- }
6188
- chooseValue(value) { const choice = this.choices.find(item => Object.is(item.value, value)); if (choice)
6189
- this.select(choice);
6190
- else
6191
- this.changed.emit(value); }
6192
- select(item) { if (this.disabled || this.readonly || !item)
6193
- return; this.changed.emit(item.value); this.itemSelected.emit(item.original); }
6194
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: SelectionControlComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
6195
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: SelectionControlComponent, isStandalone: true, selector: "vl-selection-control", inputs: { items: "items" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `
6196
- <select *ngIf="control==='dropdown'" [id]="inputId" [ngModel]="value" (ngModelChange)="chooseValue($event)"
6197
- [ngModelOptions]="{standalone:true}" [disabled]="disabled||readonly" (blur)="blurred.emit()"
6198
- [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)">
6199
- <option [ngValue]="null" [disabled]="option.required">{{placeholder || 'Select a value'}}</option>
6200
- <option *ngFor="let item of choices" [ngValue]="item.value">{{item.label}}</option>
6201
- </select>
6202
- <div *ngIf="control==='radiobutton'" class="radio-list" role="radiogroup" [attr.aria-label]="option.ariaLabel || 'Options'"
6203
- [style.flex-direction]="option.orientation==='horizontal'?'row':'column'">
6204
- <label *ngFor="let item of choices;let index=index"><input type="radio" [name]="inputId" [id]="inputId+'-'+index"
6205
- [checked]="equal(value,item.value)" [disabled]="disabled||readonly" (change)="select(item)" (blur)="blurred.emit()" />{{item.label}}</label>
6206
- </div>
6207
- <p-autocomplete *ngIf="control==='combobox'||control==='autocomplete'" [inputId]="inputId" styleClass="custom-autocomplete" [class]="classes(inputStyle)" [ngModel]="display"
6208
- (ngModelChange)="typed($event)" [ngModelOptions]="{standalone:true}" [suggestions]="suggestions" optionLabel="label"
6209
- (completeMethod)="complete($event)" (onSelect)="select($event.value)" (onBlur)="blurred.emit()"
6210
- [dropdown]="option.showDropdown !== false" [forceSelection]="option.forceSelection ?? (control==='combobox'&&option.allowCustom!==true)"
6211
- [minLength]="option.minChars ?? 1" [delay]="option.delay ?? 300"
6212
- [showClear]="option.showClear ?? false" [completeOnFocus]="option.completeOnFocus ?? false"
6213
- [dropdownMode]="option.dropdownMode || 'blank'" [placeholder]="placeholder" [disabled]="disabled||readonly"
6214
- [vlCustomKeyboard]="keyboard" appendTo="body" [ngStyle]="styles(inputStyle)"></p-autocomplete>`, isInline: true, styles: [":host{display:block;min-width:0}.radio-list{display:flex;gap:10px}.radio-list label{display:flex;gap:6px;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }] });
6215
- }
6216
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: SelectionControlComponent, decorators: [{
6217
- type: Component,
6218
- args: [{ selector: 'vl-selection-control', standalone: true, imports: [CommonModule, FormsModule, AutoCompleteModule, CustomKeyboardDirective], template: `
6219
- <select *ngIf="control==='dropdown'" [id]="inputId" [ngModel]="value" (ngModelChange)="chooseValue($event)"
6220
- [ngModelOptions]="{standalone:true}" [disabled]="disabled||readonly" (blur)="blurred.emit()"
6221
- [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)">
6222
- <option [ngValue]="null" [disabled]="option.required">{{placeholder || 'Select a value'}}</option>
6223
- <option *ngFor="let item of choices" [ngValue]="item.value">{{item.label}}</option>
6224
- </select>
6225
- <div *ngIf="control==='radiobutton'" class="radio-list" role="radiogroup" [attr.aria-label]="option.ariaLabel || 'Options'"
6226
- [style.flex-direction]="option.orientation==='horizontal'?'row':'column'">
6227
- <label *ngFor="let item of choices;let index=index"><input type="radio" [name]="inputId" [id]="inputId+'-'+index"
6228
- [checked]="equal(value,item.value)" [disabled]="disabled||readonly" (change)="select(item)" (blur)="blurred.emit()" />{{item.label}}</label>
6229
- </div>
6230
- <p-autocomplete *ngIf="control==='combobox'||control==='autocomplete'" [inputId]="inputId" styleClass="custom-autocomplete" [class]="classes(inputStyle)" [ngModel]="display"
6231
- (ngModelChange)="typed($event)" [ngModelOptions]="{standalone:true}" [suggestions]="suggestions" optionLabel="label"
6232
- (completeMethod)="complete($event)" (onSelect)="select($event.value)" (onBlur)="blurred.emit()"
6233
- [dropdown]="option.showDropdown !== false" [forceSelection]="option.forceSelection ?? (control==='combobox'&&option.allowCustom!==true)"
6234
- [minLength]="option.minChars ?? 1" [delay]="option.delay ?? 300"
6235
- [showClear]="option.showClear ?? false" [completeOnFocus]="option.completeOnFocus ?? false"
6236
- [dropdownMode]="option.dropdownMode || 'blank'" [placeholder]="placeholder" [disabled]="disabled||readonly"
6237
- [vlCustomKeyboard]="keyboard" appendTo="body" [ngStyle]="styles(inputStyle)"></p-autocomplete>`, styles: [":host{display:block;min-width:0}.radio-list{display:flex;gap:10px}.radio-list label{display:flex;gap:6px;align-items:center}\n"] }]
6238
- }], propDecorators: { items: [{
6239
- type: Input
6240
- }] } });
6241
-
6242
- /** Calendar values are wall-clock Dates. Date-only values never undergo timezone conversion. */
6243
- function wallParts(date, zone) {
6244
- const parts = new Intl.DateTimeFormat('en-CA', { timeZone: zone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h23' }).formatToParts(date);
6245
- const read = (key) => Number(parts.find(p => p.type === key)?.value);
6246
- return ['year', 'month', 'day', 'hour', 'minute', 'second'].map(read);
6247
- }
6248
- function pickerDate(value, type, zone) {
6249
- if (value === null || value === undefined || value === '')
6250
- return null;
6251
- const local = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2}))?)?$/.exec(`${value}`);
6252
- if (type === 'date' && local)
6253
- return new Date(+local[1], +local[2] - 1, +local[3]);
6254
- const clock = /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(`${value}`);
6255
- if (type === 'time' && clock)
6256
- return new Date(2000, 0, 1, +clock[1], +clock[2], +(clock[3] || 0));
6257
- const date = value instanceof Date ? value : new Date(value);
6258
- if (Number.isNaN(date.getTime()))
6259
- return null;
6260
- if (type === 'date' || !zone || zone === 'local')
6261
- return date;
6262
- const p = wallParts(date, zone);
6263
- return new Date(p[0], p[1] - 1, p[2], p[3], p[4], p[5]);
6264
- }
6265
- function instantFromWall(value, zone) {
6266
- const target = Date.UTC(value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds());
6267
- let instant = target;
6268
- for (let i = 0; i < 4; i++) {
6269
- const p = wallParts(new Date(instant), zone);
6270
- const represented = Date.UTC(p[0], p[1] - 1, p[2], p[3], p[4], p[5]);
6271
- const delta = target - represented;
6272
- if (!delta)
6273
- return new Date(instant);
6274
- instant += delta;
6275
- }
6276
- return null; // nonexistent local time during a daylight-saving jump
6277
- }
6278
- function storedTemporal(value, previous, type, option) {
6279
- if (!value)
6280
- return null;
6281
- const pad = (n) => `${n}`.padStart(2, '0');
6282
- const date = `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;
6283
- const time = `${pad(value.getHours())}:${pad(value.getMinutes())}` + (option.showSeconds ? `:${pad(value.getSeconds())}` : '');
6284
- const format = option.valueFormat ?? (typeof previous === 'string' ? (type === 'date' ? 'date' : type === 'time' && /^\d\d:\d\d/.test(previous) ? 'time' : 'iso') : 'dateObject');
6285
- if (type === 'date')
6286
- return format === 'dateObject' ? new Date(value.getFullYear(), value.getMonth(), value.getDate()) : date;
6287
- if (type === 'time' && format === 'time')
6288
- return time;
6289
- if (format === 'local')
6290
- return `${date}T${time}`;
6291
- const instant = option.displayTimezone && option.displayTimezone !== 'local' ? instantFromWall(value, option.displayTimezone) : value;
6292
- if (!instant)
6293
- throw new Error('This time does not exist in the selected timezone.');
6294
- return format === 'iso' ? instant.toISOString() : instant;
6295
- }
6296
- function primeDateFormat(format) {
6297
- if (typeof format !== 'string' || !format || ['date', 'date-time', 'iso-date', 'iso-time', 'iso-datetime'].includes(format))
6298
- return 'yy-mm-dd';
6299
- // Accept the project's common display tokens; PrimeNG uses yy for a four-digit year.
6300
- return format.replace(/\s+[Hh].*$/, '').replace(/yyyy/g, 'yy').replace(/MMMM/g, 'MM').replace(/MMM/g, 'M').replace(/MM(?=[/.-])/g, 'mm').replace(/([/.-])MM/g, '$1mm');
6301
- }
6302
-
6303
- class TemporalControlComponent extends ControlBase {
6304
- picker = inject(DateTimePickerService);
6305
- host = inject((ElementRef));
6306
- error = '';
6307
- get dateValue() { try {
6308
- return pickerDate(this.value, this.control, this.option.displayTimezone);
6309
- }
6310
- catch {
6311
- return null;
6312
- } }
6313
- get minDate() { return this.option.minDate ? pickerDate(this.option.minDate, this.control, this.option.displayTimezone) ?? undefined : undefined; }
6314
- get maxDate() { return this.option.maxDate ? pickerDate(this.option.maxDate, this.control, this.option.displayTimezone) ?? undefined : undefined; }
6315
- get displayFormat() { return primeDateFormat(this.option.displayFormat ?? this.property.format); }
6316
- choose(value) {
6317
- if (this.disabled || this.readonly)
6318
- return;
6319
- try {
6320
- const stored = storedTemporal(value, this.value, this.control, this.option);
6321
- this.error = '';
6322
- this.changed.emit(stored);
6323
- }
6324
- catch (error) {
6325
- this.error = error instanceof Error ? error.message : 'Invalid date or timezone';
6326
- this.changed.emit('invalid');
6327
- }
6328
- }
6329
- ngOnDestroy() { this.picker.closeIfWithin(this.host.nativeElement); }
6330
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: TemporalControlComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
6331
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: TemporalControlComponent, isStandalone: true, selector: "vl-temporal-control", usesInheritance: true, ngImport: i0, template: `
6332
- <div class="picker-field">
6333
- <p-datepicker #calendar styleClass="custom-datepicker" [inputId]="inputId" [ngModel]="dateValue" (ngModelChange)="choose($event)"
6334
- [ngModelOptions]="{standalone:true}" [disabled]="disabled||readonly" [readonlyInput]="option.allowManualInput === false"
6335
- [timeOnly]="control==='time'" [showTime]="control==='datetime'" [showSeconds]="option.showSeconds ?? false"
6336
- [hourFormat]="option.hourFormat || '24'" [dateFormat]="displayFormat" [placeholder]="placeholder"
6337
- [minDate]="minDate" [maxDate]="maxDate" [showClear]="option.showClear ?? true"
6338
- [stepHour]="option.stepHour ?? 1" [stepMinute]="option.stepMinute ?? 1" [stepSecond]="option.stepSecond ?? 1"
6339
- [showIcon]="false" [showOnFocus]="false" appendTo="body" [autoZIndex]="true" [baseZIndex]="10000"
6340
- [panelStyleClass]="control==='time'?picker.timePanelClass:picker.datePanelClass" [motionOptions]="picker.dockedMotion"
6341
- [vlCustomKeyboard]="option.allowManualInput===false?null:keyboard"
6342
- (pointerdown)="picker.onFieldPointerDown($event,calendar)" (onFocus)="picker.onFieldFocus($event)"
6343
- (onShow)="picker.onShown()" (onClose)="picker.onClose(calendar)" (onBlur)="blurred.emit()"
6344
- [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)"></p-datepicker>
6345
- <button type="button" class="picker-field__icon" [disabled]="disabled||readonly" [attr.aria-label]="control==='time'?'Open time picker':'Open calendar'"
6346
- (pointerdown)="picker.openFromIcon($event,calendar)" (click)="picker.blockIconClick($event)"><i [class]="control==='time'?'pi pi-clock':'pi pi-calendar'"></i></button>
6347
- </div>
6348
- <small *ngIf="error" role="alert">{{error}}</small>`, isInline: true, styles: [":host{display:block;min-width:0}.picker-field{position:relative}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }] });
6349
- }
6350
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: TemporalControlComponent, decorators: [{
6351
- type: Component,
6352
- args: [{ selector: 'vl-temporal-control', standalone: true, imports: [CommonModule, FormsModule, DatePickerModule, CustomKeyboardDirective], template: `
6353
- <div class="picker-field">
6354
- <p-datepicker #calendar styleClass="custom-datepicker" [inputId]="inputId" [ngModel]="dateValue" (ngModelChange)="choose($event)"
6355
- [ngModelOptions]="{standalone:true}" [disabled]="disabled||readonly" [readonlyInput]="option.allowManualInput === false"
6356
- [timeOnly]="control==='time'" [showTime]="control==='datetime'" [showSeconds]="option.showSeconds ?? false"
6357
- [hourFormat]="option.hourFormat || '24'" [dateFormat]="displayFormat" [placeholder]="placeholder"
6358
- [minDate]="minDate" [maxDate]="maxDate" [showClear]="option.showClear ?? true"
6359
- [stepHour]="option.stepHour ?? 1" [stepMinute]="option.stepMinute ?? 1" [stepSecond]="option.stepSecond ?? 1"
6360
- [showIcon]="false" [showOnFocus]="false" appendTo="body" [autoZIndex]="true" [baseZIndex]="10000"
6361
- [panelStyleClass]="control==='time'?picker.timePanelClass:picker.datePanelClass" [motionOptions]="picker.dockedMotion"
6362
- [vlCustomKeyboard]="option.allowManualInput===false?null:keyboard"
6363
- (pointerdown)="picker.onFieldPointerDown($event,calendar)" (onFocus)="picker.onFieldFocus($event)"
6364
- (onShow)="picker.onShown()" (onClose)="picker.onClose(calendar)" (onBlur)="blurred.emit()"
6365
- [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)"></p-datepicker>
6366
- <button type="button" class="picker-field__icon" [disabled]="disabled||readonly" [attr.aria-label]="control==='time'?'Open time picker':'Open calendar'"
6367
- (pointerdown)="picker.openFromIcon($event,calendar)" (click)="picker.blockIconClick($event)"><i [class]="control==='time'?'pi pi-clock':'pi pi-calendar'"></i></button>
6368
- </div>
6369
- <small *ngIf="error" role="alert">{{error}}</small>`, styles: [":host{display:block;min-width:0}.picker-field{position:relative}\n"] }]
6370
- }] });
6371
-
6372
- class MediaControlComponent extends ControlBase {
6373
- failed = false;
6374
- get source() {
6375
- const source = this.option.src ?? this.value;
6376
- if (typeof source === 'string')
6377
- return source;
6378
- return source?.src ?? source?.dataUrl ?? (source?.base64 ? `data:${source.mimeType || 'application/octet-stream'};base64,${source.base64}` : '');
6379
- }
6380
- get kind() {
6381
- const mime = this.option.mimeType ?? /^data:([^;,]+)/i.exec(this.source)?.[1] ?? this.value?.mimeType ?? '';
6382
- if (mime.startsWith('image/'))
6383
- return 'image';
6384
- if (mime.startsWith('video/'))
6385
- return 'video';
6386
- if (mime.startsWith('audio/'))
6387
- return 'audio';
6388
- if (mime === 'application/pdf')
6389
- return 'pdf';
6390
- const path = (this.option.fileName || this.source).split(/[?#]/)[0];
6391
- const ext = /\.([a-z0-9]+)$/i.exec(path)?.[1]?.toLowerCase();
6392
- if (['mp4', 'webm', 'mov', 'm4v', 'ogv', 'mkv', 'avi'].includes(ext || ''))
6393
- return 'video';
6394
- if (['mp3', 'wav', 'ogg', 'oga', 'm4a', 'aac', 'flac', 'opus'].includes(ext || ''))
6395
- return 'audio';
6396
- if (ext === 'pdf')
6397
- return 'pdf';
6398
- if (['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'rar', '7z', 'txt', 'csv'].includes(ext || ''))
6399
- return 'file';
6400
- return 'image';
6401
- }
6402
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MediaControlComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
6403
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: MediaControlComponent, isStandalone: true, selector: "vl-media-control", usesInheritance: true, ngImport: i0, template: `
6404
- <ng-container *ngIf="source" [ngSwitch]="kind">
6405
- <img *ngSwitchDefault [src]="source" [alt]="option.alt || property.caption || ''" [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)" loading="lazy" (error)="failed=true" />
6406
- <video *ngSwitchCase="'video'" [src]="source" [poster]="option.poster || ''" [controls]="option.controls !== false" [muted]="option.muted ?? false"
6407
- [loop]="option.loop ?? false" [autoplay]="option.autoplay ?? false" [preload]="option.preload || 'metadata'" playsinline (error)="failed=true"
6408
- [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)"></video>
6409
- <audio *ngSwitchCase="'audio'" [src]="source" [controls]="option.controls !== false" [muted]="option.muted ?? false"
6410
- [loop]="option.loop ?? false" [autoplay]="option.autoplay ?? false" [preload]="option.preload || 'metadata'" (error)="failed=true"
6411
- [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)"></audio>
6412
- <a *ngSwitchCase="'pdf'" [href]="source" target="_blank" rel="noopener noreferrer">{{option.linkLabel || 'Open PDF'}}</a>
6413
- <a *ngSwitchCase="'file'" [href]="source" target="_blank" rel="noopener noreferrer">{{option.linkLabel || 'Open file'}}</a>
6414
- </ng-container>
6415
- <small *ngIf="failed" role="alert">{{option.errorMessage || 'This media could not be loaded.'}}</small>`, isInline: true, styles: [":host{display:block;min-width:0}img,video,audio{max-width:100%}small{color:#b91c1c}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }] });
6416
- }
6417
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MediaControlComponent, decorators: [{
6418
- type: Component,
6419
- args: [{ selector: 'vl-media-control', standalone: true, imports: [CommonModule], template: `
6420
- <ng-container *ngIf="source" [ngSwitch]="kind">
6421
- <img *ngSwitchDefault [src]="source" [alt]="option.alt || property.caption || ''" [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)" loading="lazy" (error)="failed=true" />
6422
- <video *ngSwitchCase="'video'" [src]="source" [poster]="option.poster || ''" [controls]="option.controls !== false" [muted]="option.muted ?? false"
6423
- [loop]="option.loop ?? false" [autoplay]="option.autoplay ?? false" [preload]="option.preload || 'metadata'" playsinline (error)="failed=true"
6424
- [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)"></video>
6425
- <audio *ngSwitchCase="'audio'" [src]="source" [controls]="option.controls !== false" [muted]="option.muted ?? false"
6426
- [loop]="option.loop ?? false" [autoplay]="option.autoplay ?? false" [preload]="option.preload || 'metadata'" (error)="failed=true"
6427
- [ngStyle]="styles(inputStyle)" [class]="classes(inputStyle)"></audio>
6428
- <a *ngSwitchCase="'pdf'" [href]="source" target="_blank" rel="noopener noreferrer">{{option.linkLabel || 'Open PDF'}}</a>
6429
- <a *ngSwitchCase="'file'" [href]="source" target="_blank" rel="noopener noreferrer">{{option.linkLabel || 'Open file'}}</a>
6430
- </ng-container>
6431
- <small *ngIf="failed" role="alert">{{option.errorMessage || 'This media could not be loaded.'}}</small>`, styles: [":host{display:block;min-width:0}img,video,audio{max-width:100%}small{color:#b91c1c}\n"] }]
6432
- }] });
6433
-
6434
- let nextFieldId = 0;
6435
- class FieldRendererComponent {
6436
- field = {};
6437
- property = {};
6438
- items;
6439
- translations = {};
6440
- errorMessages = {};
6441
- showErrors = false;
6442
- itemSelected = new EventEmitter();
6443
- fieldBlur = new EventEmitter();
6444
- value = null;
6445
- touched = false;
6446
- formDisabled = false;
6447
- inputId = `vl-shared-field-${++nextFieldId}`;
6448
- classes = classes;
6449
- styles = styles;
6450
- onChange = () => { };
6451
- onTouched = () => { };
6452
- validatorChanged = () => { };
6453
- get option() { return this.field?.option || {}; }
6454
- get type() { return basicType(this.property); }
6455
- get control() { return controlFor(this.property, this.option); }
6456
- get allowed() { return supported(this.property, this.option); }
6457
- get keyboard() { return keyboardFor(this.property, this.option); }
6458
- get disabled() { return this.formDisabled || this.option.disabled === true; }
6459
- get readonly() { return this.option.readonly ?? this.property?.readonly ?? false; }
6460
- get protectedValue() { return this.option.protected === true; }
6461
- get label() {
6462
- const label = this.field?.label;
6463
- const caption = typeof label === 'string' ? label : label?.value ?? label?.caption ?? this.property?.caption ?? '';
6464
- return this.translations?.[caption] ?? caption;
6465
- }
6466
- get afterLabel() { return ['checkbox', 'switch'].includes(this.control) && (this.option.labelPosition === 'right' || this.option.postfixCaption === true); }
6467
- get innerStyle() {
6468
- // Same layout defaults as ModelComponent.getFieldInnerLayoutStyle().
6469
- const layoutStyle = this.field?.layoutStyle || {};
6470
- if (this.field?.layout === 'horizontal') {
6471
- return {
6472
- display: 'grid',
6473
- gridTemplateColumns: layoutStyle.gridTemplateColumns || layoutStyle['grid-template-columns'] || '80px minmax(0, 1fr)',
6474
- alignItems: layoutStyle.alignItems || 'center',
6475
- gap: layoutStyle.gap || '10px',
6476
- width: '100%', minWidth: '0', boxSizing: 'border-box',
6477
- ...layoutStyle
6478
- };
6479
- }
6480
- return { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)',
6481
- gap: layoutStyle.gap || '6px', width: '100%', minWidth: '0',
6482
- boxSizing: 'border-box', ...layoutStyle };
6483
- }
6484
- get resolvedOption() {
6485
- const p = this.property;
6486
- return {
6487
- ...this.option, placeholder: this.translations?.[this.option.placeholder ?? p.placeholder] ?? this.option.placeholder ?? p.placeholder,
6488
- currency: this.option.currency ?? this.option.fieldData?.currency ?? p.currency,
6489
- required: this.option.required ?? p.required
6490
- };
6491
- }
6492
- get errors() { return this.disabled ? null : validateValue(this.value, this.property, this.option); }
6493
- get errorText() {
6494
- const key = Object.keys(this.errors || {})[0];
6495
- if (!key)
6496
- return '';
6497
- const custom = this.option.messages?.[key];
6498
- const defaults = { required: 'This field is required.', boolean: 'Choose a boolean value.', email: 'Enter a valid email address.', number: 'Enter a valid number.', integer: 'Enter a whole number.', minimum: 'The value is below the minimum.', maximum: 'The value exceeds the maximum.', minlength: 'The value is too short.', maxlength: 'The value is too long.', pattern: 'The value does not match the required format.', enum: 'Choose an allowed value.', decimals: 'Too many decimal places.', multipleOf: 'Enter a permitted increment.', date: 'Enter a valid date or time.', minDate: 'Choose a later date or time.', maxDate: 'Choose an earlier date or time.', unsupportedControl: 'This control is not supported for this property type.', configuration: 'The field configuration is invalid.' };
6499
- const text = custom ?? this.errorMessages?.[key] ?? defaults[key] ?? 'Invalid value';
6500
- const translated = this.translations?.[text] ?? text;
6501
- const details = this.errors?.[key] || {};
6502
- return `${translated}`.replace(/{{\s*field\s*}}/g, this.label).replace(/{{\s*requiredLength\s*}}/g, `${details.requiredLength ?? ''}`);
6503
- }
6504
- get formattedValue() {
6505
- if (this.control === 'password')
6506
- return this.value ? '••••••••' : '';
6507
- const found = choicesFor(this.property, this.option, this.items).find(c => Object.is(c.value, this.value));
6508
- if (found)
6509
- return found.label;
6510
- if (this.value instanceof Date)
6511
- return this.value.toLocaleString();
6512
- return this.value == null ? '' : typeof this.value === 'object' ? JSON.stringify(this.value) : `${this.value}`;
6513
- }
6514
- ngOnChanges() { this.validatorChanged(); }
6515
- writeValue(value) { this.value = value; }
6516
- registerOnChange(fn) { this.onChange = fn; }
6517
- registerOnTouched(fn) { this.onTouched = fn; }
6518
- setDisabledState(disabled) { this.formDisabled = disabled; }
6519
- registerOnValidatorChange(fn) { this.validatorChanged = fn; }
6520
- validate(_control) { return this.errors; }
6521
- change(value) {
6522
- if (this.disabled || this.readonly || this.protectedValue)
6523
- return;
6524
- this.value = value;
6525
- this.onChange(value);
6526
- this.validatorChanged();
6527
- }
6528
- blur() { this.touched = true; this.onTouched(); this.fieldBlur.emit(); }
6529
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: FieldRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6530
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: FieldRendererComponent, isStandalone: true, selector: "vl-field-renderer", inputs: { field: "field", property: "property", items: "items", translations: "translations", errorMessages: "errorMessages", showErrors: "showErrors" }, outputs: { itemSelected: "itemSelected", fieldBlur: "fieldBlur" }, host: { properties: { "class.form-group": "type !== 'media'", "class.mb-3": "type !== 'media'" } }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FieldRendererComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => FieldRendererComponent), multi: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"option.visible !== false\" [ngStyle]=\"innerStyle\"\n [attr.aria-disabled]=\"disabled\">\n <label *ngIf=\"label && !afterLabel\"\n [attr.for]=\"control==='radiobutton'?null:inputId\"\n [ngStyle]=\"styles(field.label?.style)\"\n [class]=\"classes(field.label?.style)\">\n {{label}}<span *ngIf=\"resolvedOption.required && option.showRequiredIndicator === true\" aria-hidden=\"true\"> *</span>\n <span *ngIf=\"option.tooltip\" class=\"help\"\n [matTooltip]=\"translations[option.tooltip] || option.tooltip\" tabindex=\"0\"\n [attr.aria-label]=\"translations[option.tooltip] || option.tooltip\">\u24D8</span>\n </label>\n <div class=\"control-area\"\n [attr.aria-invalid]=\"errors && (touched||showErrors)?'true':null\"\n [attr.aria-describedby]=\"errors && (touched||showErrors)?inputId+'-error':null\">\n <div *ngIf=\"!allowed\" role=\"alert\">{{errorText}}</div>\n <span *ngIf=\"allowed && protectedValue && type!=='media'\"\n [ngStyle]=\"styles(field.scope?.style)\">{{formattedValue}}</span>\n <ng-container *ngIf=\"allowed && (!protectedValue || type==='media')\">\n <vl-text-control\n *ngIf=\"type==='string' && ['textInput','textArea','password','email'].includes(control)\"\n [value]=\"value\" [property]=\"property\" [option]=\"resolvedOption\"\n [control]=\"control\" [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-text-control>\n <vl-numeric-control\n *ngIf=\"['number','integer','bigdecimal'].includes(type)\" [value]=\"value\"\n [property]=\"property\" [option]=\"resolvedOption\" [control]=\"control\"\n [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-numeric-control>\n <vl-toggle-control *ngIf=\"['checkbox','switch'].includes(control)\"\n [value]=\"value\" [property]=\"property\" [option]=\"resolvedOption\"\n [control]=\"control\" [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-toggle-control>\n <vl-selection-control\n *ngIf=\"['dropdown','combobox','autocomplete','radiobutton'].includes(control)\"\n [value]=\"value\" [property]=\"property\" [option]=\"resolvedOption\"\n [control]=\"control\" [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\" [items]=\"items\"\n (itemSelected)=\"itemSelected.emit($event)\"></vl-selection-control>\n <vl-temporal-control *ngIf=\"['date','time','datetime'].includes(type)\"\n [value]=\"value\" [property]=\"property\" [option]=\"resolvedOption\"\n [control]=\"control\" [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-temporal-control>\n <vl-media-control *ngIf=\"type==='media'\" [value]=\"value\"\n [property]=\"property\" [option]=\"resolvedOption\" [control]=\"control\"\n [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-media-control>\n </ng-container>\n <label *ngIf=\"label && afterLabel\" [attr.for]=\"inputId\"\n [ngStyle]=\"styles(field.label?.style)\"\n [class]=\"classes(field.label?.style)\">{{label}}</label>\n <small *ngIf=\"errors && (touched||showErrors)\" [id]=\"inputId+'-error'\"\n class=\"error text-danger\" role=\"alert\"\n [ngStyle]=\"styles(field.validation?.style)\">{{errorText}}</small>\n </div>\n</div>\n", styles: [":host{display:block;min-width:0}.control-area{min-width:0}.error{display:block}.help{margin-inline-start:5px;cursor:help}.control-area>label{margin-inline-start:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i17.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: TextControlComponent, selector: "vl-text-control" }, { kind: "component", type: NumericControlComponent, selector: "vl-numeric-control" }, { kind: "component", type: ToggleControlComponent, selector: "vl-toggle-control" }, { kind: "component", type: SelectionControlComponent, selector: "vl-selection-control", inputs: ["items"] }, { kind: "component", type: TemporalControlComponent, selector: "vl-temporal-control" }, { kind: "component", type: MediaControlComponent, selector: "vl-media-control" }] });
6531
- }
6532
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: FieldRendererComponent, decorators: [{
6533
- type: Component,
6534
- args: [{ selector: 'vl-field-renderer', standalone: true, imports: [CommonModule, MatTooltipModule, TextControlComponent, NumericControlComponent, ToggleControlComponent, SelectionControlComponent, TemporalControlComponent, MediaControlComponent], host: { '[class.form-group]': "type !== 'media'", '[class.mb-3]': "type !== 'media'" }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FieldRendererComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => FieldRendererComponent), multi: true }], template: "<div *ngIf=\"option.visible !== false\" [ngStyle]=\"innerStyle\"\n [attr.aria-disabled]=\"disabled\">\n <label *ngIf=\"label && !afterLabel\"\n [attr.for]=\"control==='radiobutton'?null:inputId\"\n [ngStyle]=\"styles(field.label?.style)\"\n [class]=\"classes(field.label?.style)\">\n {{label}}<span *ngIf=\"resolvedOption.required && option.showRequiredIndicator === true\" aria-hidden=\"true\"> *</span>\n <span *ngIf=\"option.tooltip\" class=\"help\"\n [matTooltip]=\"translations[option.tooltip] || option.tooltip\" tabindex=\"0\"\n [attr.aria-label]=\"translations[option.tooltip] || option.tooltip\">\u24D8</span>\n </label>\n <div class=\"control-area\"\n [attr.aria-invalid]=\"errors && (touched||showErrors)?'true':null\"\n [attr.aria-describedby]=\"errors && (touched||showErrors)?inputId+'-error':null\">\n <div *ngIf=\"!allowed\" role=\"alert\">{{errorText}}</div>\n <span *ngIf=\"allowed && protectedValue && type!=='media'\"\n [ngStyle]=\"styles(field.scope?.style)\">{{formattedValue}}</span>\n <ng-container *ngIf=\"allowed && (!protectedValue || type==='media')\">\n <vl-text-control\n *ngIf=\"type==='string' && ['textInput','textArea','password','email'].includes(control)\"\n [value]=\"value\" [property]=\"property\" [option]=\"resolvedOption\"\n [control]=\"control\" [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-text-control>\n <vl-numeric-control\n *ngIf=\"['number','integer','bigdecimal'].includes(type)\" [value]=\"value\"\n [property]=\"property\" [option]=\"resolvedOption\" [control]=\"control\"\n [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-numeric-control>\n <vl-toggle-control *ngIf=\"['checkbox','switch'].includes(control)\"\n [value]=\"value\" [property]=\"property\" [option]=\"resolvedOption\"\n [control]=\"control\" [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-toggle-control>\n <vl-selection-control\n *ngIf=\"['dropdown','combobox','autocomplete','radiobutton'].includes(control)\"\n [value]=\"value\" [property]=\"property\" [option]=\"resolvedOption\"\n [control]=\"control\" [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\" [items]=\"items\"\n (itemSelected)=\"itemSelected.emit($event)\"></vl-selection-control>\n <vl-temporal-control *ngIf=\"['date','time','datetime'].includes(type)\"\n [value]=\"value\" [property]=\"property\" [option]=\"resolvedOption\"\n [control]=\"control\" [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-temporal-control>\n <vl-media-control *ngIf=\"type==='media'\" [value]=\"value\"\n [property]=\"property\" [option]=\"resolvedOption\" [control]=\"control\"\n [inputId]=\"inputId\"\n [inputStyle]=\"field.scope?.style\" [disabled]=\"disabled\"\n [readonly]=\"readonly\" [keyboard]=\"keyboard\"\n (changed)=\"change($event)\" (blurred)=\"blur()\"></vl-media-control>\n </ng-container>\n <label *ngIf=\"label && afterLabel\" [attr.for]=\"inputId\"\n [ngStyle]=\"styles(field.label?.style)\"\n [class]=\"classes(field.label?.style)\">{{label}}</label>\n <small *ngIf=\"errors && (touched||showErrors)\" [id]=\"inputId+'-error'\"\n class=\"error text-danger\" role=\"alert\"\n [ngStyle]=\"styles(field.validation?.style)\">{{errorText}}</small>\n </div>\n</div>\n", styles: [":host{display:block;min-width:0}.control-area{min-width:0}.error{display:block}.help{margin-inline-start:5px;cursor:help}.control-area>label{margin-inline-start:8px}\n"] }]
6535
- }], propDecorators: { field: [{
6536
- type: Input
6537
- }], property: [{
6538
- type: Input
6539
- }], items: [{
6540
- type: Input
6541
- }], translations: [{
6542
- type: Input
6543
- }], errorMessages: [{
6544
- type: Input
6545
- }], showErrors: [{
6546
- type: Input
6547
- }], itemSelected: [{
6548
- type: Output
6549
- }], fieldBlur: [{
6550
- type: Output
6551
- }] } });
6552
-
6553
6090
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
6554
6091
  // - fix: a field without a placeholder stays empty; it showed the text "undefined" (resolvePlaceholder)
6555
6092
  // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
@@ -6614,6 +6151,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
6614
6151
  type: Input
6615
6152
  }] } });
6616
6153
 
6154
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
6155
+ // - fix: a field's backend list (option.autocomplete: url, method, params) is requested whenever the
6156
+ // field has that source, whatever its data type and control - autocomplete reads it as its
6157
+ // suggestions, dropdown and combobox as their items (agreed with the developer). The work order
6158
+ // fields are type string and were never requested. {{userInfo.x}} in the parameters resolves -
6159
+ // resourceNo was sent empty
6160
+ // - fix: the autocomplete request runs under the waiting rule of every request (stagedRequest:
6161
+ // hints after 10 and 30 s, given up after 60 s); on failure the list is empty, so the spinner no
6162
+ // longer turns for ever
6163
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
6164
+ // - change: the date and time icons open the picker with showOverlay() and markForCheck(). PrimeNG 21
6165
+ // DatePicker has no show(); the call threw, the catch swallowed it, and the overlay set in finally
6166
+ // stayed invisible (the picker is OnPush) until the user tapped the field - which then showed the
6167
+ // calendar together with a keyboard: the "two keyboards" seen on iOS
6168
+ // - change: a second picker icon is no longer ignored while another picker is open. The open picker
6169
+ // is closed without focusing its field, and its late onClose no longer cancels the new one
6170
+ // - change: after an icon tap a click that no longer hits the icon is dropped. Closing the keyboard
6171
+ // resizes the dialog under the finger, and the click opened the autocomplete moved there
6172
+ // - change: an icon tap releases the field that has the focus. Its preventDefault kept an
6173
+ // autocomplete focused, so its list and the system keyboard stayed open beside the calendar
6174
+ // - new: keyboardTypeFor, so every field gets the toolkit keyboard of its attribute type
6175
+ // - new: on the devices the date picker docks at the bottom, where the keyboard would be, and the
6176
+ // dialog ends above it; the small time picker stays at its field. Tapping into a field with an
6177
+ // open picker opens the keyboard at once
6178
+ // - change: after a selection in a picker the field gives up the focus PrimeNG returns to it; it
6179
+ // showed a focus frame without any keyboard
6180
+ // - change: the docked date picker has no open/close animation; it made the panel jump up to its
6181
+ // field while closing
6182
+ // - new: the field of an open picker keeps a focus frame (picker-field--active), since the field
6183
+ // itself gives the focus away
6184
+ // - change: the picker logic moved to DateTimePickerService, shared with form, card and grid
6185
+ // - new: ngOnDestroy closes a picker that is still open, so closing the dialog cannot leave the
6186
+ // keyboard service blocked and the next dialog cut short
6187
+ // Shuaib 2026-09-24
6188
+ // - new: renderFieldType maps legacy checkbox fields to boolean.
6617
6189
  class ModelComponent {
6618
6190
  cdr;
6619
6191
  dialogRef;
@@ -6664,7 +6236,6 @@ class ModelComponent {
6664
6236
  });
6665
6237
  }
6666
6238
  ngOnDestroy() {
6667
- this.projectLanguage?.unsubscribe?.();
6668
6239
  /*
6669
6240
  * The dialog can be closed with a picker still open. Without this the keyboard service keeps
6670
6241
  * "picker open" and the reserved bottom area: no toolkit keyboard opens any more and the next
@@ -6693,7 +6264,6 @@ class ModelComponent {
6693
6264
  if (this.modalItemInfo?.currentRecord) {
6694
6265
  this.mapDataToPropertySchema(this.modalItemInfo.currentRecord, this.modalItemInfo);
6695
6266
  }
6696
- this.initializeSharedDefaults();
6697
6267
  await this.setAutoCompleteData();
6698
6268
  }
6699
6269
  updateDialogHeader() {
@@ -6833,8 +6403,8 @@ class ModelComponent {
6833
6403
  }
6834
6404
  }
6835
6405
  autocompSelectEvent(obj, formField) {
6836
- const mapping = formField?.option?.onSelect?.mapFields;
6837
- if (!mapping || obj == null)
6406
+ const mapping = formField.option?.onSelect?.mapFields;
6407
+ if (!mapping)
6838
6408
  return;
6839
6409
  Object.entries(mapping).forEach(([targetProp, sourceKey]) => {
6840
6410
  const scope = this.getScope(formField.scope.reference);
@@ -7034,17 +6604,15 @@ class ModelComponent {
7034
6604
  const scope = this.getScope(formField.scope.reference);
7035
6605
  if (rule?.resetValue?.length > 0) {
7036
6606
  rule.resetValue.forEach((prop) => {
7037
- if (this.modalItemInfo.propertySchema[scope]?.[prop]) {
7038
- this.modalItemInfo.propertySchema[scope][prop].value = null;
7039
- }
6607
+ this.modalItemInfo.propertySchema[scope][prop].value = null;
7040
6608
  });
7041
6609
  }
7042
6610
  }
7043
6611
  isRuleTriggered(rule, formField) {
7044
6612
  if (rule.when === 'timeRangeComplete') {
7045
6613
  const scope = this.getScope(formField.scope.reference);
7046
- return (this.modalItemInfo.propertySchema[scope]?.[formField.scope.reference.split('/').pop()]?.value &&
7047
- this.modalItemInfo.propertySchema[scope]?.[rule.with || 'workDayTimeTo']?.value);
6614
+ return (this.modalItemInfo.propertySchema[scope]?.workDayTimeFrom?.value &&
6615
+ this.modalItemInfo.propertySchema[scope]?.workDayTimeTo?.value);
7048
6616
  }
7049
6617
  else if (rule.when === 'change') {
7050
6618
  return true;
@@ -7054,47 +6622,37 @@ class ModelComponent {
7054
6622
  applyDurationRule(rule, formField) {
7055
6623
  const scope = this.getScope(formField.scope.reference);
7056
6624
  const schema = this.modalItemInfo.propertySchema[scope];
7057
- const currentKey = formField.scope.reference.split('/').pop();
7058
- // Explicit keys support other time pairs; existing work-order schemas keep their meaning.
7059
- const fromKey = rule.params?.fromTime || (schema.workDayTimeFrom ? 'workDayTimeFrom' : currentKey);
7060
- const toKey = rule.params?.toTime || (schema.workDayTimeTo ? 'workDayTimeTo' : rule.with);
7061
- const fromDateKey = rule.params?.fromDate || 'fromDate';
7062
- const toDateKey = rule.params?.toDate || 'toDate';
7063
- const durationKey = rule.params?.durationField;
7064
- if (!durationKey || !schema?.[durationKey])
7065
- return;
7066
- const fromTime = this.ruleClock(schema?.[fromKey]?.value);
7067
- const toTime = this.ruleClock(schema?.[toKey]?.value);
7068
- const baseValue = schema?.[fromDateKey]?.value;
7069
- const dateOnly = typeof baseValue === 'string' && /^(\d{4})-(\d{2})-(\d{2})$/.exec(baseValue);
7070
- const baseDate = dateOnly ? new Date(+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3])
7071
- : baseValue == null || baseValue === '' ? null : new Date(baseValue);
7072
- if (!fromTime || !toTime || !baseDate || Number.isNaN(baseDate.getTime())) {
7073
- schema[durationKey].value = '';
6625
+ const fromTimeVal = schema?.workDayTimeFrom?.value;
6626
+ const toTimeVal = schema?.workDayTimeTo?.value;
6627
+ const baseDateVal = schema?.fromDate?.value;
6628
+ // 🔒 Guard
6629
+ if (!fromTimeVal || !toTimeVal || !baseDateVal) {
6630
+ schema[rule.params.durationField].value = '';
7074
6631
  return;
7075
6632
  }
6633
+ // 📅 Base date (From Date)
6634
+ const baseDate = new Date(baseDateVal);
6635
+ baseDate.setHours(0, 0, 0, 0);
6636
+ // ⏱ FROM datetime
7076
6637
  const fromDateTime = new Date(baseDate);
7077
- fromDateTime.setHours(fromTime[0], fromTime[1], 0, 0);
7078
- const toDateTime = new Date(baseDate);
7079
- toDateTime.setHours(toTime[0], toTime[1], 0, 0);
7080
- // Preserve the existing overnight rule, including equal times meaning next day.
7081
- if (toDateTime <= fromDateTime)
6638
+ fromDateTime.setHours(fromTimeVal.getHours(), fromTimeVal.getMinutes(), 0, 0);
6639
+ // ⏱ TO datetime
6640
+ let toDateTime = new Date(baseDate);
6641
+ toDateTime.setHours(toTimeVal.getHours(), toTimeVal.getMinutes(), 0, 0);
6642
+ // 🌙 Overnight handling
6643
+ if (toDateTime <= fromDateTime) {
7082
6644
  toDateTime.setDate(toDateTime.getDate() + 1);
7083
- const totalMinutes = Math.floor((toDateTime.getTime() - fromDateTime.getTime()) / 60000);
7084
- schema[durationKey].value = `${Math.floor(totalMinutes / 60)}:${`${totalMinutes % 60}`.padStart(2, '0')}`;
7085
- if (schema[toDateKey])
7086
- schema[toDateKey].value = new Date(toDateTime);
7087
- }
7088
- ruleClock(value) {
7089
- if (value == null || value === '')
7090
- return null;
7091
- if (typeof value === 'string') {
7092
- const time = /^(\d{2}):(\d{2})(?::\d{2})?$/.exec(value);
7093
- if (time)
7094
- return +time[1] < 24 && +time[2] < 60 ? [+time[1], +time[2]] : null;
7095
6645
  }
7096
- const date = value instanceof Date ? value : new Date(value);
7097
- return Number.isNaN(date.getTime()) ? null : [date.getHours(), date.getMinutes()];
6646
+ // ⏳ Duration calculation
6647
+ const diffMs = toDateTime.getTime() - fromDateTime.getTime();
6648
+ const totalMinutes = Math.floor(diffMs / 60000);
6649
+ const hours = Math.floor(totalMinutes / 60);
6650
+ const minutes = totalMinutes % 60;
6651
+ const duration = `${hours}:${minutes.toString().padStart(2, '0')}`;
6652
+ // ✅ Set duration
6653
+ schema[rule.params.durationField].value = duration;
6654
+ // ✅ Auto set To Date
6655
+ schema.toDate.value = new Date(toDateTime);
7098
6656
  }
7099
6657
  invokeSetformData() {
7100
6658
  void this.setFormDataHandler?.();
@@ -7119,7 +6677,6 @@ class ModelComponent {
7119
6677
  }
7120
6678
  }
7121
6679
  checkValidty(form) {
7122
- this.sharedErrorsVisible = true;
7123
6680
  if (!form) {
7124
6681
  return false;
7125
6682
  }
@@ -7205,49 +6762,12 @@ class ModelComponent {
7205
6762
  }
7206
6763
  return 'image';
7207
6764
  }
7208
- sharedErrorsVisible = false;
7209
- sharedProperty(field) {
7210
- const reference = field?.scope?.reference;
7211
- if (typeof reference !== 'string')
7212
- return undefined;
7213
- const parts = reference.replace(/^#\//, '').split('/');
7214
- const root = this.modalItemInfo?.propertySchema?.[parts[0]];
7215
- const key = parts[parts.length - 1];
7216
- return root?.[key] ?? root?.properties?.[key];
7217
- }
7218
- usesSharedField(field) {
7219
- const property = this.sharedProperty(field);
7220
- return !!property && Object.prototype.hasOwnProperty.call(CONTROL_MAP, basicType(property));
7221
- }
7222
- initializeSharedDefaults() {
7223
- for (const section of this.modalItemInfo?.layoutSchema?.formSections || []) {
7224
- for (const field of section.formFields || []) {
7225
- const property = this.sharedProperty(field);
7226
- if (!property || property.value !== undefined)
7227
- continue;
7228
- const value = field.option?.default ?? field.option?.fieldData?.default ?? property.default;
7229
- if (value !== undefined)
7230
- property.value = structuredClone(value);
7231
- }
7232
- }
7233
- }
7234
- onSharedFieldChange(field, value) {
7235
- const property = this.sharedProperty(field);
7236
- if (!property)
7237
- return;
7238
- property.value = value;
7239
- // Existing date/time rules and boolean copy actions continue through their original methods.
7240
- if (field.option?.rules)
7241
- this.handleRules(field, value);
7242
- if (basicType(property) === 'boolean')
7243
- this.invokeSetformData();
7244
- }
7245
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ModelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$4.DynamicDialogRef }, { token: i1$4.DynamicDialogConfig }, { token: UtilityService }], target: i0.ɵɵFactoryTarget.Component });
7246
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: ModelComponent, isStandalone: true, selector: "vl-model", inputs: { errorMessages: "errorMessages", modalItemInfo: "modalItemInfo", selectedLanguageInfo: "selectedLanguageInfo", userInfo: "userInfo", setFormDataHandler: "setFormDataHandler" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\n project defines none; it showed the text \"undefined\" -->\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\n - change: number, autocomplete, date and time fields take the toolkit keyboard of their\n attribute type (keyboardTypeFor) instead of only an explicit option.keyboardType\n - change: the date picker docks at the bottom on the devices (model-datepicker-panel--docked);\n the time picker stays attached to its field in a compact form (model-timepicker-panel);\n both report which picker closed (onPickerClose(picker)) -->\n\n<!-- Shuaib 2026-09-24\n - change: the model field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\n\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\n - new: a @contract comment on the widest type coverage in the toolkit, and one on the credit\n card case recording that the rename reached this file first -->\n<!-- Loader Spinner -->\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\n <div class=\"spinner\"></div>\n</div>\n<div [style]=\"getStyleWithoutClasses(modalItemInfo?.layoutSchema?.style).style\"\n [class]=\"modalItemInfo?.layoutSchema?.style?.classes[0]\">\n <form #dynamicForm=\"ngForm\"\n *ngIf=\"modalItemInfo?.layoutSchema?.formSections?.length > 0\"\n (submit)=\"$event.preventDefault()\">\n <div\n *ngFor=\"let formSection of modalItemInfo?.layoutSchema?.formSections; let sectionIndex = index\">\n <div *ngIf=\"formSection?.label?.caption\"\n [class]=\"formSection?.label?.style?.classes[0]\"\n [style]=\"getStyleWithoutClasses(formSection?.label?.style).style\">\n {{formSection.label.caption}}\n </div>\n <div *ngIf=\"formSection?.formFields?.length > 0\"\n [class]=\"formSection?.style?.classes?.[0] || ''\"\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\n <div *ngFor=\"let formField of formSection.formFields; let fieldIndex = index\"\n [ngStyle]=\"getFieldWrapperStyle(formField)\"\n [style.display]=\"usesSharedField(formField) && formField.option?.visible === false ? 'none' : null\"\n [class]=\"formField?.style?.classes || ''\">\n\n <!-- @contract decision: the widest type coverage in the toolkit\n reality: every field type except the grid-only quantity renders here.\n When another container drops a type, this is the way out.\n seen: measured 2026-09-19. Note the file is model.component.html while\n the container is called modal in layout files - that costs a\n search every time -->\n <!-- @contract compatibility: checkbox is rendered as boolean for existing projects. -->\n @if (usesSharedField(formField)) {\n @if (sharedProperty(formField); as property) {\n @if (formField.option?.visible !== false) {\n <vl-field-renderer [field]=\"formField\" [property]=\"property\"\n [ngModel]=\"property.value\" (ngModelChange)=\"onSharedFieldChange(formField,$event)\"\n [name]=\"'shared-' + sectionIndex + '-' + fieldIndex + '-' + formField.scope.reference\" [disabled]=\"formField.option?.disabled === true\"\n [items]=\"formField.data ?? property.data\" [translations]=\"selectedLanguageInfo?.selectedLanguage || {}\"\n [errorMessages]=\"errorMessages\" [showErrors]=\"sharedErrorsVisible\"\n (itemSelected)=\"autocompSelectEvent($event,formField)\">\n </vl-field-renderer>\n }\n }\n } @else {\n <div [ngSwitch]=\"renderFieldType(\n formField?.scope?.reference &&\n modalItemInfo?.propertySchema?.[\n getScope(formField.scope.reference)\n ]\n ? modalItemInfo.propertySchema[\n getScope(formField.scope.reference)\n ]?.[\n formField.scope.reference.split('/').pop()\n ]?.type\n : 'default'\n )\">\n <!-- Media control -->\n \n\n <!-- credit card control -->\n <!-- @contract decision: the credit card type is spelled creditCard\n reality: this file was renamed on 2026-09-15, form.component.html and\n utility.service.ts followed on 2026-09-19. carditCard is read\n nowhere any more.\n failure: a data object still declaring carditCard renders empty. -->\n <div *ngSwitchCase=\"'creditCard'\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [class]=\"formField.style?.classes[0]\">\n <div id=\"card-element\">\n <!-- a Stripe element will be inserted here -->\n </div>\n <!-- Used to display form errors -->\n <div id=\"card-errors\"\n class=\"displayError.textContent!=='': 'alert alert-danger mt-1'\">\n </div>\n </div>\n\n <!-- string control -->\n \n\n <!-- number control -->\n \n\n <!-- DateRange control -->\n <div *ngSwitchCase=\"'dateRange'\"\n class=\"form-group mb-3\">\n <span *ngIf=\"formField.option.protected\"\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\n <vl-string [formField]=\"formField\"\n [modalItemInfo]=\"modalItemInfo\">\n </vl-string>\n </span>\n <div *ngIf=\"!formField.option.protected\">\n <div\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\n <label\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\n formField.label?.value\n ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\n }}</label>\n <nz-range-picker #dateRangeField=\"ngModel\"\n [required]=\"formField.option.required\"\n [name]=\"formField.scope?.reference\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\n </div>\n <!-- Error Message -->\n <div\n *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\n <p class=\"text-danger\"\n *ngIf=\"dateRangeField?.errors?.['required']\">\n {{formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\n +\n ' ' + errorMessages.required}}</p>\n <p class=\"text-danger\"\n *ngIf=\"dateRangeField?.errors?.['email']\">\n {{(formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\n +\n ' ' + errorMessages.pattern}}</p>\n </div>\n </div>\n </div>\n\n <!-- Date control -->\n \n\n <!-- Time control -->\n \n\n <!-- Autocomplete control -->\n <div *ngSwitchCase=\"'autocomplete'\"\n class=\"form-group mb-3\">\n <span *ngIf=\"formField.option.protected\"\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\n <vl-string [formField]=\"formField\"\n [modalItemInfo]=\"modalItemInfo\">\n </vl-string>\n </span>\n <div *ngIf=\"!formField.option.protected\">\n <div\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\n <vl-label [formField]=\"formField\"\n [modalItemInfo]=\"modalItemInfo\">\n </vl-label>\n\n <span>\n <p-autocomplete\n #autocompleteField=\"ngModel\"\n [required]=\"formField.option.required\"\n [name]=\"formField.scope?.reference\"\n styleClass=\"custom-autocomplete\"\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\n [suggestions]=\"matchData\"\n [dropdown]=\"true\"\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\n [placeholder]=\"modalItemInfo.propertySchema | vlPlaceholder: formField.scope?.reference\"\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\n </p-autocomplete>\n <!-- Error Message -->\n <div\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\n <p class=\"text-danger\"\n *ngIf=\"autocompleteField?.errors?.['required']\">\n {{setErroMessage(formField,\n errorMessages, \"required\")}}</p>\n <p class=\"text-danger\"\n *ngIf=\"autocompleteField?.errors?.['email']\">\n {{(formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\n +\n ' ' +\n errorMessages.pattern}}</p>\n </div>\n </span>\n </div>\n </div>\n </div>\n\n <!-- checkBox control -->\n \n\n <!-- dropdown control -->\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group mb-3\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\n <span *ngIf=\"formField.option?.postfixCaption\">\n <select id=\"dynamic-dropdown\"\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\n #dropdownField=\"ngModel\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [name]=\"formField.scope?.reference\"\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\n <option\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\n {{ formField?.option?.optionLabel ?\n formField.option.optionLabel :\n 'label'}}</option>\n </select>\n <label\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\n }}</label>\n </span>\n <div *ngIf=\"!formField.option?.postfixCaption\">\n <div\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\n <label\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\n }}</label>\n\n <nz-select nzShowSearch nzAllowClear\n nzPlaceHolder=\"Select\"\n #dropdownField=\"ngModel\"\n [required]=\"formField.option.required\"\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [name]=\"formField.scope?.reference\"\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\n <nz-option\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\n </nz-select>\n </div>\n <!-- Error Message -->\n <div\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\n <p class=\"text-danger\"\n *ngIf=\"dropdownField?.errors?.['required']\">\n {{formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\n +\n ' ' + errorMessages.required}}</p>\n <p class=\"text-danger\"\n *ngIf=\"dropdownField?.errors?.['email']\">\n {{(formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\n +\n ' ' + errorMessages.pattern}}</p>\n </div>\n </div>\n </div>\n\n <!-- button control -->\n <div *ngSwitchCase=\"'default'\">\n <button type=\"button\"\n [ngStyle]=\"getStyleWithoutClasses(formField.style).style\"\n (click)=\"invokeFormBtnClick(formField.option, modalItemInfo.propertySchema, dynamicForm)\"\n class=\"btn btn-primary btn-block\">\n {{ getButtonCaption(formField) }}\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </form>\n</div>\n", styles: [":host ::ng-deep .custom-datepicker .p-datepicker-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-datepicker .p-datepicker-input{border-radius:4px}::ng-deep .p-autocomplete-list{background-color:#fff}:host ::ng-deep .custom-autocomplete{display:flex;width:100%}:host ::ng-deep .custom-autocomplete .p-autocomplete-input{flex:1;width:100%;min-width:0;border:none!important;outline:none!important;box-shadow:none!important;background:transparent!important}:host ::ng-deep .custom-autocomplete .p-autocomplete-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus,:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus-visible,:host ::ng-deep .custom-autocomplete .p-inputtext:enabled:focus{border:none!important;outline:none!important;box-shadow:none!important}.responsive-modal-grid{width:100%;min-width:0;box-sizing:border-box}@media screen and (max-width:767px){.responsive-modal-grid{grid-template-columns:repeat(12,minmax(0,1fr))!important;gap:10px!important}.responsive-modal-field{grid-column:1 / -1!important;width:100%!important;max-width:none!important;min-width:0!important}.responsive-modal-action:nth-last-child(2){grid-column:1 / span 6!important}.responsive-modal-action:last-child{grid-column:7 / span 6!important}.responsive-modal-action{width:100%!important;max-width:none!important;min-width:0!important;justify-self:stretch!important;box-sizing:border-box!important}.responsive-modal-action button{display:block!important;width:100%!important;max-width:none!important;min-width:0!important;margin:0!important;box-sizing:border-box!important}}:host{--modal-field-focus-color: var(--p-primary-color, #10b981);--modal-field-focus-shadow: 0 0 0 2px rgba(16, 185, 129, .16)}:host ::ng-deep input:not([type=checkbox]):not(:disabled):focus,:host ::ng-deep textarea:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within .p-autocomplete-input{border:none!important;outline:none!important;box-shadow:none!important}:host ::ng-deep select:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .ant-select-focused:not(.ant-select-disabled) .ant-select-selector,:host ::ng-deep nz-select:not(.ant-select-disabled):focus-within .ant-select-selector{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .p-select:not(.p-disabled).p-focus,:host ::ng-deep .p-select:not(.p-disabled):focus-within,:host ::ng-deep .p-dropdown:not(.p-disabled).p-focus,:host ::ng-deep .p-dropdown:not(.p-disabled):focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-datepicker:focus-within .p-datepicker-input{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep input:disabled,:host ::ng-deep textarea:disabled,:host ::ng-deep select:disabled,:host ::ng-deep .p-disabled,:host ::ng-deep .ant-select-disabled .ant-select-selector{box-shadow:none!important}\n"], dependencies: [{ kind: "component", type: FieldRendererComponent, selector: "vl-field-renderer", inputs: ["field", "property", "items", "translations", "errorMessages", "showErrors"], outputs: ["itemSelected", "fieldBlur"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzVariant", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzPrefix", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus", "nzOnClear"], exportAs: ["nzSelect"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "component", type: Label, selector: "vl-label", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: PlaceholderPipe, name: "vlPlaceholder" }] });
6765
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ModelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$3.DynamicDialogRef }, { token: i1$3.DynamicDialogConfig }, { token: UtilityService }], target: i0.ɵɵFactoryTarget.Component });
6766
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: ModelComponent, isStandalone: true, selector: "vl-model", inputs: { errorMessages: "errorMessages", modalItemInfo: "modalItemInfo", selectedLanguageInfo: "selectedLanguageInfo", userInfo: "userInfo", setFormDataHandler: "setFormDataHandler" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: number, autocomplete, date and time fields take the toolkit keyboard of their\r\n attribute type (keyboardTypeFor) instead of only an explicit option.keyboardType\r\n - change: the date picker docks at the bottom on the devices (model-datepicker-panel--docked);\r\n the time picker stays attached to its field in a compact form (model-timepicker-panel);\r\n both report which picker closed (onPickerClose(picker)) -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the model field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the widest type coverage in the toolkit, and one on the credit\r\n card case recording that the rename reached this file first -->\r\n<!-- Loader Spinner -->\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div [style]=\"getStyleWithoutClasses(modalItemInfo?.layoutSchema?.style).style\"\r\n [class]=\"modalItemInfo?.layoutSchema?.style?.classes[0]\">\r\n <form #dynamicForm=\"ngForm\"\r\n *ngIf=\"modalItemInfo?.layoutSchema?.formSections?.length > 0\"\r\n (submit)=\"$event.preventDefault()\">\r\n <div\r\n *ngFor=\"let formSection of modalItemInfo?.layoutSchema?.formSections\">\r\n <div *ngIf=\"formSection?.label?.caption\"\r\n [class]=\"formSection?.label?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(formSection?.label?.style).style\">\r\n {{formSection.label.caption}}\r\n </div>\r\n <div *ngIf=\"formSection?.formFields?.length > 0\"\r\n [class]=\"formSection?.style?.classes?.[0] || ''\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\"\r\n [class]=\"formField?.style?.classes || ''\">\r\n\r\n <!-- @contract decision: the widest type coverage in the toolkit\r\n reality: every field type except the grid-only quantity renders here.\r\n When another container drops a type, this is the way out.\r\n seen: measured 2026-09-19. Note the file is model.component.html while\r\n the container is called modal in layout files - that costs a\r\n search every time -->\r\n <!-- @contract compatibility: checkbox is rendered as boolean for existing projects. -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n modalItemInfo?.propertySchema?.[\r\n getScope(formField.scope.reference)\r\n ]\r\n ? modalItemInfo.propertySchema[\r\n getScope(formField.scope.reference)\r\n ]?.[\r\n formField.scope.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- Media control -->\r\n <div\r\n *ngSwitchCase=\"'media'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n\r\n <ng-container\r\n *ngTemplateOutlet=\"modelMediaContent; context: { field: formField }\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: this file was renamed on 2026-09-15, form.component.html and\r\n utility.service.ts followed on 2026-09-19. carditCard is read\r\n nowhere any more.\r\n failure: a data object still declaring carditCard renders empty. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes[0]\">\r\n <div id=\"card-element\">\r\n <!-- a Stripe element will be inserted here -->\r\n </div>\r\n <!-- Used to display form errors -->\r\n <div id=\"card-errors\"\r\n class=\"displayError.textContent!=='': 'alert alert-danger mt-1'\">\r\n </div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group mb-3\">\r\n @if(formField.option && formField.option.control &&\r\n !formField.option.protected){\r\n @switch (formField.option.control) {\r\n @case ('textInput') {\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n }\r\n\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema | vlPlaceholder: formField.scope?.reference\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n }\r\n }\r\n }@else{\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n </div>\r\n }\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <h5>{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:formField.option.currency :true:\r\n formField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n [placeholder]=\"modalItemInfo.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #datePicker\r\n #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [dateFormat]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #timePicker\r\n #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"onTimePanelShow(formField)\"\r\n (onClose)=\"picker.onClose(timePicker)\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema | vlPlaceholder: formField.scope?.reference\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- checkBox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <label class=\"form-check-label\"><input\r\n type=\"checkbox\" class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"invokeSetformData()\" />{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\"\r\n [class]=\"formField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label class=\"form-check-label\">{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n <input type=\"checkbox\"\r\n class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\" /></label>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"button\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField.style).style\"\r\n (click)=\"invokeFormBtnClick(formField.option, modalItemInfo.propertySchema, dynamicForm)\"\r\n class=\"btn btn-primary btn-block\">\r\n {{ getButtonCaption(formField) }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n<ng-template #modelMediaContent let-field=\"field\">\r\n <ng-container *ngIf=\"getMediaSource(field) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(field)\">\r\n\r\n <img\r\n *ngSwitchDefault\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(field)\"\r\n loading=\"lazy\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(field)\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(field)\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>", styles: [":host ::ng-deep .custom-datepicker .p-datepicker-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-datepicker .p-datepicker-input{border-radius:4px}::ng-deep .p-autocomplete-list{background-color:#fff}:host ::ng-deep .custom-autocomplete{display:flex;width:100%}:host ::ng-deep .custom-autocomplete .p-autocomplete-input{flex:1;width:100%;min-width:0;border:none!important;outline:none!important;box-shadow:none!important;background:transparent!important}:host ::ng-deep .custom-autocomplete .p-autocomplete-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus,:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus-visible,:host ::ng-deep .custom-autocomplete .p-inputtext:enabled:focus{border:none!important;outline:none!important;box-shadow:none!important}.responsive-modal-grid{width:100%;min-width:0;box-sizing:border-box}@media screen and (max-width:767px){.responsive-modal-grid{grid-template-columns:repeat(12,minmax(0,1fr))!important;gap:10px!important}.responsive-modal-field{grid-column:1 / -1!important;width:100%!important;max-width:none!important;min-width:0!important}.responsive-modal-action:nth-last-child(2){grid-column:1 / span 6!important}.responsive-modal-action:last-child{grid-column:7 / span 6!important}.responsive-modal-action{width:100%!important;max-width:none!important;min-width:0!important;justify-self:stretch!important;box-sizing:border-box!important}.responsive-modal-action button{display:block!important;width:100%!important;max-width:none!important;min-width:0!important;margin:0!important;box-sizing:border-box!important}}:host{--modal-field-focus-color: var(--p-primary-color, #10b981);--modal-field-focus-shadow: 0 0 0 2px rgba(16, 185, 129, .16)}:host ::ng-deep input:not([type=checkbox]):not(:disabled):focus,:host ::ng-deep textarea:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within .p-autocomplete-input{border:none!important;outline:none!important;box-shadow:none!important}:host ::ng-deep select:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .ant-select-focused:not(.ant-select-disabled) .ant-select-selector,:host ::ng-deep nz-select:not(.ant-select-disabled):focus-within .ant-select-selector{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .p-select:not(.p-disabled).p-focus,:host ::ng-deep .p-select:not(.p-disabled):focus-within,:host ::ng-deep .p-dropdown:not(.p-disabled).p-focus,:host ::ng-deep .p-dropdown:not(.p-disabled):focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-datepicker:focus-within .p-datepicker-input{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep input:disabled,:host ::ng-deep textarea:disabled,:host ::ng-deep select:disabled,:host ::ng-deep .p-disabled,:host ::ng-deep .ant-select-disabled .ant-select-selector{box-shadow:none!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i5.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzVariant", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzPrefix", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus", "nzOnClear"], exportAs: ["nzSelect"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "component", type: Label, selector: "vl-label", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: Textbox, selector: "vl-textbox", inputs: ["formField", "modalItemInfo"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: PlaceholderPipe, name: "vlPlaceholder" }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }] });
7247
6767
  }
7248
6768
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ModelComponent, decorators: [{
7249
6769
  type: Component,
7250
- args: [{ standalone: true, selector: 'vl-model', imports: [FieldRendererComponent, PlaceholderPipe,
6770
+ args: [{ standalone: true, selector: 'vl-model', imports: [PlaceholderPipe,
7251
6771
  CommonModule,
7252
6772
  FormsModule,
7253
6773
  NzSelectModule,
@@ -7259,8 +6779,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
7259
6779
  String$1,
7260
6780
  Textbox,
7261
6781
  CustomKeyboardDirective,
7262
- ], template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\n project defines none; it showed the text \"undefined\" -->\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\n - change: number, autocomplete, date and time fields take the toolkit keyboard of their\n attribute type (keyboardTypeFor) instead of only an explicit option.keyboardType\n - change: the date picker docks at the bottom on the devices (model-datepicker-panel--docked);\n the time picker stays attached to its field in a compact form (model-timepicker-panel);\n both report which picker closed (onPickerClose(picker)) -->\n\n<!-- Shuaib 2026-09-24\n - change: the model field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\n\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\n - new: a @contract comment on the widest type coverage in the toolkit, and one on the credit\n card case recording that the rename reached this file first -->\n<!-- Loader Spinner -->\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\n <div class=\"spinner\"></div>\n</div>\n<div [style]=\"getStyleWithoutClasses(modalItemInfo?.layoutSchema?.style).style\"\n [class]=\"modalItemInfo?.layoutSchema?.style?.classes[0]\">\n <form #dynamicForm=\"ngForm\"\n *ngIf=\"modalItemInfo?.layoutSchema?.formSections?.length > 0\"\n (submit)=\"$event.preventDefault()\">\n <div\n *ngFor=\"let formSection of modalItemInfo?.layoutSchema?.formSections; let sectionIndex = index\">\n <div *ngIf=\"formSection?.label?.caption\"\n [class]=\"formSection?.label?.style?.classes[0]\"\n [style]=\"getStyleWithoutClasses(formSection?.label?.style).style\">\n {{formSection.label.caption}}\n </div>\n <div *ngIf=\"formSection?.formFields?.length > 0\"\n [class]=\"formSection?.style?.classes?.[0] || ''\"\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\n <div *ngFor=\"let formField of formSection.formFields; let fieldIndex = index\"\n [ngStyle]=\"getFieldWrapperStyle(formField)\"\n [style.display]=\"usesSharedField(formField) && formField.option?.visible === false ? 'none' : null\"\n [class]=\"formField?.style?.classes || ''\">\n\n <!-- @contract decision: the widest type coverage in the toolkit\n reality: every field type except the grid-only quantity renders here.\n When another container drops a type, this is the way out.\n seen: measured 2026-09-19. Note the file is model.component.html while\n the container is called modal in layout files - that costs a\n search every time -->\n <!-- @contract compatibility: checkbox is rendered as boolean for existing projects. -->\n @if (usesSharedField(formField)) {\n @if (sharedProperty(formField); as property) {\n @if (formField.option?.visible !== false) {\n <vl-field-renderer [field]=\"formField\" [property]=\"property\"\n [ngModel]=\"property.value\" (ngModelChange)=\"onSharedFieldChange(formField,$event)\"\n [name]=\"'shared-' + sectionIndex + '-' + fieldIndex + '-' + formField.scope.reference\" [disabled]=\"formField.option?.disabled === true\"\n [items]=\"formField.data ?? property.data\" [translations]=\"selectedLanguageInfo?.selectedLanguage || {}\"\n [errorMessages]=\"errorMessages\" [showErrors]=\"sharedErrorsVisible\"\n (itemSelected)=\"autocompSelectEvent($event,formField)\">\n </vl-field-renderer>\n }\n }\n } @else {\n <div [ngSwitch]=\"renderFieldType(\n formField?.scope?.reference &&\n modalItemInfo?.propertySchema?.[\n getScope(formField.scope.reference)\n ]\n ? modalItemInfo.propertySchema[\n getScope(formField.scope.reference)\n ]?.[\n formField.scope.reference.split('/').pop()\n ]?.type\n : 'default'\n )\">\n <!-- Media control -->\n \n\n <!-- credit card control -->\n <!-- @contract decision: the credit card type is spelled creditCard\n reality: this file was renamed on 2026-09-15, form.component.html and\n utility.service.ts followed on 2026-09-19. carditCard is read\n nowhere any more.\n failure: a data object still declaring carditCard renders empty. -->\n <div *ngSwitchCase=\"'creditCard'\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [class]=\"formField.style?.classes[0]\">\n <div id=\"card-element\">\n <!-- a Stripe element will be inserted here -->\n </div>\n <!-- Used to display form errors -->\n <div id=\"card-errors\"\n class=\"displayError.textContent!=='': 'alert alert-danger mt-1'\">\n </div>\n </div>\n\n <!-- string control -->\n \n\n <!-- number control -->\n \n\n <!-- DateRange control -->\n <div *ngSwitchCase=\"'dateRange'\"\n class=\"form-group mb-3\">\n <span *ngIf=\"formField.option.protected\"\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\n <vl-string [formField]=\"formField\"\n [modalItemInfo]=\"modalItemInfo\">\n </vl-string>\n </span>\n <div *ngIf=\"!formField.option.protected\">\n <div\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\n <label\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\n formField.label?.value\n ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\n }}</label>\n <nz-range-picker #dateRangeField=\"ngModel\"\n [required]=\"formField.option.required\"\n [name]=\"formField.scope?.reference\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\n </div>\n <!-- Error Message -->\n <div\n *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\n <p class=\"text-danger\"\n *ngIf=\"dateRangeField?.errors?.['required']\">\n {{formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\n +\n ' ' + errorMessages.required}}</p>\n <p class=\"text-danger\"\n *ngIf=\"dateRangeField?.errors?.['email']\">\n {{(formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\n +\n ' ' + errorMessages.pattern}}</p>\n </div>\n </div>\n </div>\n\n <!-- Date control -->\n \n\n <!-- Time control -->\n \n\n <!-- Autocomplete control -->\n <div *ngSwitchCase=\"'autocomplete'\"\n class=\"form-group mb-3\">\n <span *ngIf=\"formField.option.protected\"\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\n <vl-string [formField]=\"formField\"\n [modalItemInfo]=\"modalItemInfo\">\n </vl-string>\n </span>\n <div *ngIf=\"!formField.option.protected\">\n <div\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\n <vl-label [formField]=\"formField\"\n [modalItemInfo]=\"modalItemInfo\">\n </vl-label>\n\n <span>\n <p-autocomplete\n #autocompleteField=\"ngModel\"\n [required]=\"formField.option.required\"\n [name]=\"formField.scope?.reference\"\n styleClass=\"custom-autocomplete\"\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\n [suggestions]=\"matchData\"\n [dropdown]=\"true\"\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\n [placeholder]=\"modalItemInfo.propertySchema | vlPlaceholder: formField.scope?.reference\"\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\n </p-autocomplete>\n <!-- Error Message -->\n <div\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\n <p class=\"text-danger\"\n *ngIf=\"autocompleteField?.errors?.['required']\">\n {{setErroMessage(formField,\n errorMessages, \"required\")}}</p>\n <p class=\"text-danger\"\n *ngIf=\"autocompleteField?.errors?.['email']\">\n {{(formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\n +\n ' ' +\n errorMessages.pattern}}</p>\n </div>\n </span>\n </div>\n </div>\n </div>\n\n <!-- checkBox control -->\n \n\n <!-- dropdown control -->\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group mb-3\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\n <span *ngIf=\"formField.option?.postfixCaption\">\n <select id=\"dynamic-dropdown\"\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\n #dropdownField=\"ngModel\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [name]=\"formField.scope?.reference\"\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\n <option\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\n {{ formField?.option?.optionLabel ?\n formField.option.optionLabel :\n 'label'}}</option>\n </select>\n <label\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\n }}</label>\n </span>\n <div *ngIf=\"!formField.option?.postfixCaption\">\n <div\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\n <label\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\n }}</label>\n\n <nz-select nzShowSearch nzAllowClear\n nzPlaceHolder=\"Select\"\n #dropdownField=\"ngModel\"\n [required]=\"formField.option.required\"\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\n [name]=\"formField.scope?.reference\"\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\n <nz-option\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\n </nz-select>\n </div>\n <!-- Error Message -->\n <div\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\n <p class=\"text-danger\"\n *ngIf=\"dropdownField?.errors?.['required']\">\n {{formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\n +\n ' ' + errorMessages.required}}</p>\n <p class=\"text-danger\"\n *ngIf=\"dropdownField?.errors?.['email']\">\n {{(formField.label?.value ?\n formField.label?.value :\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\n +\n ' ' + errorMessages.pattern}}</p>\n </div>\n </div>\n </div>\n\n <!-- button control -->\n <div *ngSwitchCase=\"'default'\">\n <button type=\"button\"\n [ngStyle]=\"getStyleWithoutClasses(formField.style).style\"\n (click)=\"invokeFormBtnClick(formField.option, modalItemInfo.propertySchema, dynamicForm)\"\n class=\"btn btn-primary btn-block\">\n {{ getButtonCaption(formField) }}\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </form>\n</div>\n", styles: [":host ::ng-deep .custom-datepicker .p-datepicker-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-datepicker .p-datepicker-input{border-radius:4px}::ng-deep .p-autocomplete-list{background-color:#fff}:host ::ng-deep .custom-autocomplete{display:flex;width:100%}:host ::ng-deep .custom-autocomplete .p-autocomplete-input{flex:1;width:100%;min-width:0;border:none!important;outline:none!important;box-shadow:none!important;background:transparent!important}:host ::ng-deep .custom-autocomplete .p-autocomplete-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus,:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus-visible,:host ::ng-deep .custom-autocomplete .p-inputtext:enabled:focus{border:none!important;outline:none!important;box-shadow:none!important}.responsive-modal-grid{width:100%;min-width:0;box-sizing:border-box}@media screen and (max-width:767px){.responsive-modal-grid{grid-template-columns:repeat(12,minmax(0,1fr))!important;gap:10px!important}.responsive-modal-field{grid-column:1 / -1!important;width:100%!important;max-width:none!important;min-width:0!important}.responsive-modal-action:nth-last-child(2){grid-column:1 / span 6!important}.responsive-modal-action:last-child{grid-column:7 / span 6!important}.responsive-modal-action{width:100%!important;max-width:none!important;min-width:0!important;justify-self:stretch!important;box-sizing:border-box!important}.responsive-modal-action button{display:block!important;width:100%!important;max-width:none!important;min-width:0!important;margin:0!important;box-sizing:border-box!important}}:host{--modal-field-focus-color: var(--p-primary-color, #10b981);--modal-field-focus-shadow: 0 0 0 2px rgba(16, 185, 129, .16)}:host ::ng-deep input:not([type=checkbox]):not(:disabled):focus,:host ::ng-deep textarea:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within .p-autocomplete-input{border:none!important;outline:none!important;box-shadow:none!important}:host ::ng-deep select:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .ant-select-focused:not(.ant-select-disabled) .ant-select-selector,:host ::ng-deep nz-select:not(.ant-select-disabled):focus-within .ant-select-selector{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .p-select:not(.p-disabled).p-focus,:host ::ng-deep .p-select:not(.p-disabled):focus-within,:host ::ng-deep .p-dropdown:not(.p-disabled).p-focus,:host ::ng-deep .p-dropdown:not(.p-disabled):focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-datepicker:focus-within .p-datepicker-input{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep input:disabled,:host ::ng-deep textarea:disabled,:host ::ng-deep select:disabled,:host ::ng-deep .p-disabled,:host ::ng-deep .ant-select-disabled .ant-select-selector{box-shadow:none!important}\n"] }]
7263
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$4.DynamicDialogRef }, { type: i1$4.DynamicDialogConfig }, { type: UtilityService }], propDecorators: { errorMessages: [{
6782
+ ], template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - fix: the placeholder comes from vlPlaceholder (pipes/placeholder.pipe.ts) - empty where the\r\n project defines none; it showed the text \"undefined\" -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: number, autocomplete, date and time fields take the toolkit keyboard of their\r\n attribute type (keyboardTypeFor) instead of only an explicit option.keyboardType\r\n - change: the date picker docks at the bottom on the devices (model-datepicker-panel--docked);\r\n the time picker stays attached to its field in a compact form (model-timepicker-panel);\r\n both report which picker closed (onPickerClose(picker)) -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the model field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the widest type coverage in the toolkit, and one on the credit\r\n card case recording that the rename reached this file first -->\r\n<!-- Loader Spinner -->\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div [style]=\"getStyleWithoutClasses(modalItemInfo?.layoutSchema?.style).style\"\r\n [class]=\"modalItemInfo?.layoutSchema?.style?.classes[0]\">\r\n <form #dynamicForm=\"ngForm\"\r\n *ngIf=\"modalItemInfo?.layoutSchema?.formSections?.length > 0\"\r\n (submit)=\"$event.preventDefault()\">\r\n <div\r\n *ngFor=\"let formSection of modalItemInfo?.layoutSchema?.formSections\">\r\n <div *ngIf=\"formSection?.label?.caption\"\r\n [class]=\"formSection?.label?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(formSection?.label?.style).style\">\r\n {{formSection.label.caption}}\r\n </div>\r\n <div *ngIf=\"formSection?.formFields?.length > 0\"\r\n [class]=\"formSection?.style?.classes?.[0] || ''\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\"\r\n [class]=\"formField?.style?.classes || ''\">\r\n\r\n <!-- @contract decision: the widest type coverage in the toolkit\r\n reality: every field type except the grid-only quantity renders here.\r\n When another container drops a type, this is the way out.\r\n seen: measured 2026-09-19. Note the file is model.component.html while\r\n the container is called modal in layout files - that costs a\r\n search every time -->\r\n <!-- @contract compatibility: checkbox is rendered as boolean for existing projects. -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n modalItemInfo?.propertySchema?.[\r\n getScope(formField.scope.reference)\r\n ]\r\n ? modalItemInfo.propertySchema[\r\n getScope(formField.scope.reference)\r\n ]?.[\r\n formField.scope.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- Media control -->\r\n <div\r\n *ngSwitchCase=\"'media'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n\r\n <ng-container\r\n *ngTemplateOutlet=\"modelMediaContent; context: { field: formField }\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: this file was renamed on 2026-09-15, form.component.html and\r\n utility.service.ts followed on 2026-09-19. carditCard is read\r\n nowhere any more.\r\n failure: a data object still declaring carditCard renders empty. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes[0]\">\r\n <div id=\"card-element\">\r\n <!-- a Stripe element will be inserted here -->\r\n </div>\r\n <!-- Used to display form errors -->\r\n <div id=\"card-errors\"\r\n class=\"displayError.textContent!=='': 'alert alert-danger mt-1'\">\r\n </div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group mb-3\">\r\n @if(formField.option && formField.option.control &&\r\n !formField.option.protected){\r\n @switch (formField.option.control) {\r\n @case ('textInput') {\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n }\r\n\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema | vlPlaceholder: formField.scope?.reference\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n }\r\n }\r\n }@else{\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n </div>\r\n }\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <h5>{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:formField.option.currency :true:\r\n formField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n [placeholder]=\"modalItemInfo.propertySchema | vlPlaceholder: formField.scope?.reference\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #datePicker\r\n #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [dateFormat]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #timePicker\r\n #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"onTimePanelShow(formField)\"\r\n (onClose)=\"picker.onClose(timePicker)\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema | vlPlaceholder: formField.scope?.reference\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- checkBox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <label class=\"form-check-label\"><input\r\n type=\"checkbox\" class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"invokeSetformData()\" />{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\"\r\n [class]=\"formField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label class=\"form-check-label\">{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n <input type=\"checkbox\"\r\n class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\" /></label>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"button\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField.style).style\"\r\n (click)=\"invokeFormBtnClick(formField.option, modalItemInfo.propertySchema, dynamicForm)\"\r\n class=\"btn btn-primary btn-block\">\r\n {{ getButtonCaption(formField) }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n<ng-template #modelMediaContent let-field=\"field\">\r\n <ng-container *ngIf=\"getMediaSource(field) as source\">\r\n <ng-container [ngSwitch]=\"getMediaKind(field)\">\r\n\r\n <img\r\n *ngSwitchDefault\r\n [src]=\"source\"\r\n [alt]=\"getMediaLabel(field)\"\r\n loading=\"lazy\" />\r\n\r\n <video\r\n *ngSwitchCase=\"'video'\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(field)\"\r\n controls\r\n playsinline\r\n preload=\"metadata\">\r\n Your browser does not support video playback.\r\n </video>\r\n\r\n <audio\r\n *ngSwitchCase=\"'audio'\"\r\n [src]=\"source\"\r\n [attr.aria-label]=\"getMediaLabel(field)\"\r\n controls\r\n preload=\"metadata\">\r\n Your browser does not support audio playback.\r\n </audio>\r\n\r\n <a\r\n *ngSwitchCase=\"'pdf'\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open PDF \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n\r\n <a\r\n *ngSwitchCase=\"'file'\"\r\n [href]=\"source\"\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\">\r\n Open file \u2014 {{ getMediaLabel(field) }}\r\n </a>\r\n\r\n </ng-container>\r\n </ng-container>\r\n</ng-template>", styles: [":host ::ng-deep .custom-datepicker .p-datepicker-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-datepicker .p-datepicker-input{border-radius:4px}::ng-deep .p-autocomplete-list{background-color:#fff}:host ::ng-deep .custom-autocomplete{display:flex;width:100%}:host ::ng-deep .custom-autocomplete .p-autocomplete-input{flex:1;width:100%;min-width:0;border:none!important;outline:none!important;box-shadow:none!important;background:transparent!important}:host ::ng-deep .custom-autocomplete .p-autocomplete-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus,:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus-visible,:host ::ng-deep .custom-autocomplete .p-inputtext:enabled:focus{border:none!important;outline:none!important;box-shadow:none!important}.responsive-modal-grid{width:100%;min-width:0;box-sizing:border-box}@media screen and (max-width:767px){.responsive-modal-grid{grid-template-columns:repeat(12,minmax(0,1fr))!important;gap:10px!important}.responsive-modal-field{grid-column:1 / -1!important;width:100%!important;max-width:none!important;min-width:0!important}.responsive-modal-action:nth-last-child(2){grid-column:1 / span 6!important}.responsive-modal-action:last-child{grid-column:7 / span 6!important}.responsive-modal-action{width:100%!important;max-width:none!important;min-width:0!important;justify-self:stretch!important;box-sizing:border-box!important}.responsive-modal-action button{display:block!important;width:100%!important;max-width:none!important;min-width:0!important;margin:0!important;box-sizing:border-box!important}}:host{--modal-field-focus-color: var(--p-primary-color, #10b981);--modal-field-focus-shadow: 0 0 0 2px rgba(16, 185, 129, .16)}:host ::ng-deep input:not([type=checkbox]):not(:disabled):focus,:host ::ng-deep textarea:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within .p-autocomplete-input{border:none!important;outline:none!important;box-shadow:none!important}:host ::ng-deep select:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .ant-select-focused:not(.ant-select-disabled) .ant-select-selector,:host ::ng-deep nz-select:not(.ant-select-disabled):focus-within .ant-select-selector{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .p-select:not(.p-disabled).p-focus,:host ::ng-deep .p-select:not(.p-disabled):focus-within,:host ::ng-deep .p-dropdown:not(.p-disabled).p-focus,:host ::ng-deep .p-dropdown:not(.p-disabled):focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-datepicker:focus-within .p-datepicker-input{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep input:disabled,:host ::ng-deep textarea:disabled,:host ::ng-deep select:disabled,:host ::ng-deep .p-disabled,:host ::ng-deep .ant-select-disabled .ant-select-selector{box-shadow:none!important}\n"] }]
6783
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$3.DynamicDialogRef }, { type: i1$3.DynamicDialogConfig }, { type: UtilityService }], propDecorators: { errorMessages: [{
7264
6784
  type: Input
7265
6785
  }], modalItemInfo: [{
7266
6786
  type: Input
@@ -7726,7 +7246,7 @@ class MobileKeycloakService {
7726
7246
  // Keycloak projects require a login: the restarted app shows its loader until the sign-in opens.
7727
7247
  await this.oidc.signOut(this.provider(), tokens, 'KeycloakInfo', key => this.utilityService.removeLocalStorage(key), { loginRequired: true });
7728
7248
  }
7729
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MobileKeycloakService, deps: [{ token: ConfigService }, { token: i1$2.HttpClient }, { token: UtilityService }, { token: i1.Platform }, { token: MobileOidcService }], target: i0.ɵɵFactoryTarget.Injectable });
7249
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MobileKeycloakService, deps: [{ token: ConfigService }, { token: i1$1.HttpClient }, { token: UtilityService }, { token: i1.Platform }, { token: MobileOidcService }], target: i0.ɵɵFactoryTarget.Injectable });
7730
7250
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MobileKeycloakService, providedIn: 'root' });
7731
7251
  }
7732
7252
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MobileKeycloakService, decorators: [{
@@ -7734,8 +7254,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
7734
7254
  args: [{
7735
7255
  providedIn: 'root'
7736
7256
  }]
7737
- }], ctorParameters: () => [{ type: ConfigService }, { type: i1$2.HttpClient }, { type: UtilityService }, { type: i1.Platform }, { type: MobileOidcService }] });
7257
+ }], ctorParameters: () => [{ type: ConfigService }, { type: i1$1.HttpClient }, { type: UtilityService }, { type: i1.Platform }, { type: MobileOidcService }] });
7738
7258
 
7259
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-16
7260
+ // - fix: the menu closes after a pagination choice too (it stayed open, unlike after a language)
7261
+ // - change: the menu starts closed and closes after a choice wherever the app behaves like on a device
7262
+ // (toolkitKeyboardActive - also the Designer preview with Mobile/Tablet, the tablet too, wider than
7263
+ // 768 px); sign-in stays native-only
7739
7264
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
7740
7265
  // - new: activeLanguage, the language file in use ("de.json"); its menu entry gets a check mark
7741
7266
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-11
@@ -7942,9 +7467,13 @@ class SideMenuComponent {
7942
7467
  }
7943
7468
  async ngOnInit() {
7944
7469
  this.getPaginationType();
7945
- if (this.isNativeMobile()) {
7470
+ // closed at the start and closing after a choice: on the devices and in the Designer preview
7471
+ // with "Mobile"/"Tablet" (toolkitKeyboardActive), so the preview behaves like the app
7472
+ if (toolkitKeyboardActive()) {
7946
7473
  this.isCollapsed = true;
7947
7474
  this.isMobile = true;
7475
+ }
7476
+ if (this.isNativeMobile()) {
7948
7477
  this.tenantKey =
7949
7478
  await this.utilityService.getLocalStorage('tenantKey') ?? '';
7950
7479
  }
@@ -8023,7 +7552,7 @@ class SideMenuComponent {
8023
7552
  this.mobileMode = this.computeMobileMode();
8024
7553
  }
8025
7554
  computeMobileMode() {
8026
- return (isNativeMobile() ||
7555
+ return (toolkitKeyboardActive() ||
8027
7556
  (typeof window !== 'undefined' &&
8028
7557
  window.matchMedia('(max-width: 768px), (max-height: 500px)').matches));
8029
7558
  }
@@ -8368,6 +7897,12 @@ class SideMenuComponent {
8368
7897
  if (!option) {
8369
7898
  return;
8370
7899
  }
7900
+ // Pagination chosen: like the language and any other entry, the menu closes on phone and tablet
7901
+ if (option.scrollPagination || option.numberedPagination) {
7902
+ if (this.mobileMode && !this.isCollapsed) {
7903
+ this.toggleCollapsed();
7904
+ }
7905
+ }
8371
7906
  if (option.scrollPagination) {
8372
7907
  this.scrollPagination = true;
8373
7908
  this.paginationType.emit({ scroll: true });
@@ -8554,8 +8089,8 @@ class SideMenuComponent {
8554
8089
  originalCaption;
8555
8090
  });
8556
8091
  }
8557
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: SideMenuComponent, deps: [{ token: i1$5.NzI18nService }, { token: MobileKeycloakService }, { token: Couchdb }, { token: i7.OAuthService }, { token: i1.Platform }, { token: RequestService }, { token: UtilityService }, { token: i1$4.DialogService }, { token: NotificationService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component });
8558
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: SideMenuComponent, isStandalone: true, selector: "vl-side-menu", inputs: { menuItems: "menuItems", userInfo: "userInfo", settings: "settings", projects: "projects", tenantName: "tenantName", environment: "environment", projectSeleted: "projectSeleted", appVersionInfo: "appVersionInfo" }, outputs: { languageChange: "languageChange", invokeMenu: "invokeMenu", menuToggle: "menuToggle", invokeProject: "invokeProject", errorMessage: "errorMessage", deleteProject: "deleteProject", paginationType: "paginationType" }, host: { listeners: { "window:resize": "onResizeMobileMode()", "document:click": "onDocumentClick($event)" }, properties: { "class.sidemenu-mobile": "this.mobileMode" } }, viewQueries: [{ propertyName: "sideMenu", first: true, predicate: ["sideMenu"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: the active language has a check mark, like the active pagination type\r\n - fix: all check marks (pagination, language) stand in one column at the right edge; the\r\n pagination marks followed their caption at a fixed distance and landed elsewhere on iOS\r\n - change: the version footer has three columns - version, build (source commit) and build\r\n number (appBuildNumber), the three values build.sh and the web Docker build produce -->\r\n<!-- Shuaib 2026-09-24\r\n - change: sign-in visibility checks now include jwtToken alongside keycloak and oktaAuth. -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the option key, singular since 2026-09-16, and on the sign-in\r\n branches, which know keycloak and oktaAuth but not jwtToken -->\r\n<div class=\"left-wrap sidemenu\" [class.collapsed]=\"isCollapsed\"\r\n [ngStyle]=\"{ 'z-index': !isCollapsed && isMobile ? 999999 : 'auto' }\"\r\n #sideMenu>\r\n <!-- Move toggle button OUTSIDE menu container -->\r\n <div class=\"side-toggle-handle\" id=\"toggle-id\" (click)=\"toggleCollapsed()\"\r\n [class.collapsed]=\"isCollapsed\">\r\n <span id=\"toggle-icon\" nz-icon\r\n [nzType]=\"isCollapsed ? 'caret-right' : 'caret-left'\"></span>\r\n </div>\r\n <div class=\"side-menu\" id=\"sidemenuwrap\"\r\n [ngStyle]=\"{ width: (isMobile && isCollapsed) ? '1px' : isCollapsed ? '60px' : '' }\">\r\n <!-- user content -->\r\n <ng-container *ngIf=\"loginedUserInfo && loginedUserInfo?.name\">\r\n <div class=\"user-profile\" [class.collapsed]=\"isCollapsed\">\r\n <img class=\"avatar\" [src]=\"loginedUserInfo?.image\" alt=\"User\" />\r\n <div [class]=\"isMobile ? '' : 'details'\" *ngIf=\"!isCollapsed\">\r\n <div class=\"name\">{{ loginedUserInfo.name }}</div>\r\n <div class=\"email\">{{ loginedUserInfo?.email }}</div>\r\n <div class=\"actions\" *ngIf=\"loginedUserInfo?.actions?.length\">\r\n <button *ngFor=\"let action of loginedUserInfo?.actions\" nz-button\r\n nzType=\"link\" nzSize=\"small\"\r\n (click)=\"invokeNavigate(action)\">\r\n <span>{{ action?.label?.caption }}</span>\r\n <span nz-icon [nzType]=\"action.icon\"></span>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-container>\r\n <!-- @contract decision: this container reads option (singular), like every other one\r\n reality: it read options (plural) until 2026-09-16. A menu file written for the old\r\n spelling is ignored without an error.\r\n\r\n @contract decision: sign-in visibility supports keycloak, oktaAuth and jwtToken.\r\n A JWT project uses the decoded user's login state, like the other providers.\r\n seen: the login type comparisons in the lines below -->\r\n <!-- dynamic menu -->\r\n <ul nz-menu nzMode=\"inline\" nzTheme=\"dark\"\r\n [nzInlineCollapsed]=\"isCollapsed\">\r\n <ng-container *ngFor=\"let item of menuItems\">\r\n <ng-container\r\n *ngIf=\"!item.submenu && item?.option?.action != 'logout'; else submenu\">\r\n <span\r\n [class]=\"environment?.login?.type === 'oktaAuth' || environment?.login?.type === 'keycloak' || environment?.login?.type === 'jwtToken' ? item.option?.login === true ? loginedUserInfo?.name ? '' : 'hidden' : item.option?.login === false ? loginedUserInfo?.name ? 'hidden' : '' : '' : userInfo?.userId ? item.option?.login ? item.option.role ? item.option.role == userInfo.userRole ? '' : 'hidden' : '' : 'hidden' : !item.option?.login ? '' : 'hidden' \">\r\n <li (click)=\"invokeNavigate(item)\" nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? item?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"item.icon\"></span>\r\n <span>{{ item?.label?.caption }}</span>\r\n </li>\r\n </span>\r\n </ng-container>\r\n <ng-template #submenu>\r\n <span\r\n [class]=\"item.option?.login === true && (environment?.login?.type === 'oktaAuth' || environment?.login?.type === 'keycloak' || environment?.login?.type === 'jwtToken') ? loginedUserInfo?.name ? '' : 'hidden' : item.option?.login === false ? loginedUserInfo?.name ? 'hidden' : '' : ''\">\r\n <li nz-submenu *ngIf=\"item?.option?.action != 'logout'\"\r\n [nzTitle]=\"item?.label?.caption\"\r\n [nzIcon]=\"item.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let child of item.submenu\">\r\n <li (click)=\"invokeNavigate(child)\" *ngIf=\"!child.submenu\"\r\n nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? child?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"child?.icon\"></span>\r\n <span>{{ child?.label?.caption }}</span>\r\n </li>\r\n <ng-container *ngIf=\"child.submenu\">\r\n <li nz-submenu [nzTitle]=\"child?.label?.caption\"\r\n [nzIcon]=\"child?.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let subChild of child.submenu\">\r\n <li (click)=\"invokeNavigate(subChild)\" nz-menu-item>\r\n <ng-container\r\n *ngIf=\"subChild?.option?.setImageUrl; else antIcon\">\r\n <img *ngIf=\"isUrl(subChild?.icon)\"\r\n [src]=\"subChild?.icon\" alt=\"icon\"\r\n style=\"width: 16px; height: 16px; margin-right: 8px;\" />\r\n </ng-container>\r\n <ng-template #antIcon>\r\n <span nz-icon [nzType]=\"subChild?.icon\"></span>\r\n </ng-template>\r\n <span>{{ subChild?.label?.caption }}</span>\r\n @if(subChild?.option?.numberedPagination &&\r\n !scrollPagination){\r\n <span nz-icon\r\n nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n @if(subChild?.option?.scrollPagination &&\r\n scrollPagination){\r\n <span nz-icon\r\n nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n @if(subChild?.option?.changeLang &&\r\n subChild?.option?.data_url === activeLanguage){\r\n <span nz-icon nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </span>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <!-- separator -->\r\n <li *ngIf=\"settingMenu.submenu\" class=\"menu-separator\"></li>\r\n\r\n <!-- settings menu -->\r\n <ng-container *ngIf=\"!settingMenu.submenu; else settingSubmenu\"\r\n style=\"position: absolute; bottom: 0;\">\r\n <li (click)=\"invokeNavigate(settingMenu)\" nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? settingMenu?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"settingMenu.icon\"></span>\r\n <span>{{ settingMenu?.label?.caption }}</span>\r\n </li>\r\n </ng-container>\r\n <ng-template #settingSubmenu>\r\n <li nz-submenu [nzTitle]=\"settingMenu?.label?.caption\"\r\n [nzIcon]=\"settingMenu.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let child of settingMenu.submenu\">\r\n <li (click)=\"invokeNavigate(child)\" *ngIf=\"!child.submenu\"\r\n nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? child?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"child?.icon\"></span>\r\n <span>{{ child?.label?.caption }}</span>\r\n </li>\r\n <ng-container *ngIf=\"child.submenu\">\r\n <li nz-submenu [nzTitle]=\"child?.label?.caption\"\r\n [nzIcon]=\"child?.icon\">\r\n <ul style=\"width: 260px;\">\r\n <ng-container\r\n *ngFor=\"let subChild of child.submenu; let i = index\">\r\n <li (click)=\"invokeNavigate(subChild, child)\"\r\n nz-menu-item>\r\n <span\r\n [class]=\"child?.label?.caption === 'Languages' ? '' : 'menu-row'\">\r\n <ng-container\r\n *ngIf=\"subChild?.option?.changeProject; else antIcon\">\r\n <img [src]=\"subChild?.icon\" alt=\"icon\"\r\n style=\"width: 16px; height: 16px; margin-right: 8px;\" />\r\n </ng-container>\r\n <ng-template #antIcon>\r\n <span nz-icon [nzType]=\"subChild?.icon\"></span>\r\n </ng-template>\r\n <span>{{ subChild?.label?.caption }}</span>\r\n <!-- Delete icon (prevent click bubbling) -->\r\n <span *ngIf=\"child?.label?.caption != 'Languages'\"\r\n nz-icon nzType=\"delete\" class=\"delete-icon\"\r\n style=\"padding-left: 5%;\"\r\n (click)=\"deleteMenuItem(subChild, i, $event)\"></span>\r\n </span>\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-template>\r\n </ul>\r\n @if (!isCollapsed && appVersionInfo?.appVersion) {\r\n <div class=\"app-version-footer\">\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Version</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appVersion }}</span>\r\n </div>\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Build</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appBuild || '-' }}</span>\r\n </div>\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Build No.</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appBuildNumber || '-' }}</span>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{position:relative;display:block;flex:0 0 auto;width:250px;height:100%;min-height:0;overflow:visible;transition:width .3s ease}.left-wrap.sidemenu{position:relative;width:250px;height:100%;min-height:0;overflow:visible;transition:width .3s ease,transform .3s ease}.side-menu{position:relative;display:flex;flex-direction:column;width:250px;height:100%;min-height:0;color:#fff;background-color:#0d1b2a;overflow:hidden;transition:width .3s ease,box-shadow .3s ease}:host:has(.left-wrap.sidemenu.collapsed){width:64px}.left-wrap.sidemenu.collapsed,.left-wrap.sidemenu.collapsed .side-menu{width:64px}.menu-separator{flex:0 0 auto;height:1px;margin:15px 12px 6px;background-color:#ffffff26;border:0;list-style:none}.user-profile{flex:0 0 auto;padding:20px 10px 16px;margin-top:40px;text-align:center;border-bottom:1px solid rgba(255,255,255,.1);overflow:hidden;transition:padding .25s ease,margin .25s ease,opacity .25s ease,max-height .25s ease}.user-profile .avatar{display:block;width:64px;height:64px;margin:0 auto 8px;object-fit:cover;border:2px solid #ffffff;border-radius:50%;transition:width .25s ease,height .25s ease,margin .25s ease}.user-profile .details{min-width:0;overflow:hidden;transition:opacity .2s ease}.user-profile .name{color:#fff;font-size:14px;font-weight:600;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.user-profile .email{margin:2px 0 8px;color:#aaa;font-size:12px;line-height:1.4;overflow-wrap:anywhere;word-break:break-word}.user-profile .actions{display:flex;align-items:center;justify-content:center;gap:6px}.user-profile .actions button{margin:0;color:#fff}.left-wrap.sidemenu.collapsed .user-profile{padding:12px 8px}.left-wrap.sidemenu.collapsed .user-profile .avatar{width:40px;height:40px;margin-bottom:0}.left-wrap.sidemenu.collapsed .user-profile .details,.left-wrap.sidemenu.collapsed .user-profile .name,.left-wrap.sidemenu.collapsed .user-profile .email,.left-wrap.sidemenu.collapsed .user-profile .actions{display:none}.side-menu ul[nz-menu]{flex:1 1 auto;width:100%;min-height:0;margin:0;padding:4px 0 8px;overflow-x:hidden;overflow-y:auto;background-color:transparent;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.28) transparent;scroll-behavior:smooth}.side-menu ul[nz-menu]::-webkit-scrollbar{width:6px}.side-menu ul[nz-menu]::-webkit-scrollbar-track{background:transparent}.side-menu ul[nz-menu]::-webkit-scrollbar-thumb{background-color:#ffffff47;border-radius:999px}.side-menu ul[nz-menu]::-webkit-scrollbar-thumb:hover{background-color:#ffffff6b}.menu-row{display:flex;align-items:center;width:100%;min-width:0}.menu-text{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.delete-icon{flex:0 0 auto;margin-left:auto;color:#ffffffb3;cursor:pointer;transition:color .18s ease,transform .18s ease}.delete-icon:hover{color:#ff7875;transform:scale(1.08)}.projects{position:static;flex:0 0 auto;width:100%;margin-top:auto;background-color:#0d1b2a}.side-toggle-handle{position:absolute;top:24px;right:-14px;z-index:10000;display:flex;align-items:center;justify-content:center;width:28px;height:48px;padding:0;color:#fff;background-color:#0d1b2a;border:0;border-radius:0 6px 6px 0;box-shadow:2px 0 6px #00000040;cursor:pointer;transform:translateY(-50%);transition:background-color .2s ease,box-shadow .2s ease}.side-toggle-handle:hover{background-color:#142a40;box-shadow:3px 0 9px #0000004d}.side-toggle-handle:focus-visible{outline:3px solid rgba(24,144,255,.45);outline-offset:2px}.side-toggle-handle span{color:#fff;font-size:14px;line-height:1;transition:transform .25s ease}.collapse-btn{position:absolute;top:8px;right:8px;z-index:10001}:host(:not(.sidemenu-mobile)) .left-wrap.sidemenu.collapsed:hover{transform:translate(0)}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed{width:64px!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-item,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-submenu>.ant-menu-submenu-title{display:flex!important;align-items:center!important;justify-content:center!important;width:45px!important;height:32px!important;margin-inline:0!important;padding-inline:0!important;overflow:hidden!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-title-content,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .menu-text,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .delete-icon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-submenu-arrow{display:none!important;width:0!important;min-width:0!important;max-width:0!important;margin:0!important;padding:0!important;visibility:hidden!important;overflow:hidden!important;text-overflow:clip!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-item-icon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item>.anticon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title>.anticon{display:inline-flex!important;flex:0 0 auto;align-items:center;justify-content:center;width:auto!important;min-width:18px;margin:0!important;padding:0!important;color:inherit;font-size:18px;line-height:1;visibility:visible!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .menu-row{display:contents}:host(.sidemenu-mobile){position:absolute;top:0;bottom:0;left:0;z-index:9999;width:250px;height:100%}:host(.sidemenu-mobile):has(.left-wrap.sidemenu.collapsed){width:0}:host(.sidemenu-mobile) .left-wrap.sidemenu{width:250px;height:100%}:host(.sidemenu-mobile) .side-menu,:host(.sidemenu-mobile) .side-menu.open{width:250px;height:100%;box-shadow:8px 0 22px #0000003d}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed{width:250px;transform:translate(-250px)}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .side-menu{width:250px;box-shadow:none}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .side-toggle-handle{right:-28px}:host(.sidemenu-mobile) .side-toggle-handle{top:0;right:0;padding:0;transform:none!important}:host(.sidemenu-mobile) .collapse-btn{top:8px;right:8px}:host(.sidemenu-mobile) .user-profile{margin-top:40px}:host(.sidemenu-mobile) .user-profile .avatar{width:48px;height:48px}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .user-profile{max-height:0;padding:0;margin:0;opacity:0;overflow:hidden;border-bottom:0}@media(prefers-reduced-motion:reduce){:host,.left-wrap.sidemenu,.side-menu,.user-profile,.user-profile .avatar,.side-toggle-handle,.side-toggle-handle span{transition:none}.side-menu ul[nz-menu]{scroll-behavior:auto}}.app-version-footer{flex-shrink:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:10px 12px;border-top:1px solid rgba(255,255,255,.15);color:#ffffffa6;font-size:11px;line-height:1.4;text-align:center}.app-version-footer__item{display:flex;flex-direction:column;min-width:0}.app-version-footer__label{font-size:10px;opacity:.7}.app-version-footer__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pagination-check{position:absolute;top:50%;right:42px;transform:translateY(-50%)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: PlatformModule }, { kind: "ngmodule", type:
8092
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: SideMenuComponent, deps: [{ token: i1$4.NzI18nService }, { token: MobileKeycloakService }, { token: Couchdb }, { token: i7.OAuthService }, { token: i1.Platform }, { token: RequestService }, { token: UtilityService }, { token: i1$3.DialogService }, { token: NotificationService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component });
8093
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: SideMenuComponent, isStandalone: true, selector: "vl-side-menu", inputs: { menuItems: "menuItems", userInfo: "userInfo", settings: "settings", projects: "projects", tenantName: "tenantName", environment: "environment", projectSeleted: "projectSeleted", appVersionInfo: "appVersionInfo" }, outputs: { languageChange: "languageChange", invokeMenu: "invokeMenu", menuToggle: "menuToggle", invokeProject: "invokeProject", errorMessage: "errorMessage", deleteProject: "deleteProject", paginationType: "paginationType" }, host: { listeners: { "window:resize": "onResizeMobileMode()", "document:click": "onDocumentClick($event)" }, properties: { "class.sidemenu-mobile": "this.mobileMode" } }, viewQueries: [{ propertyName: "sideMenu", first: true, predicate: ["sideMenu"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: the active language has a check mark, like the active pagination type\r\n - fix: all check marks (pagination, language) stand in one column at the right edge; the\r\n pagination marks followed their caption at a fixed distance and landed elsewhere on iOS\r\n - change: the version footer has three columns - version, build (source commit) and build\r\n number (appBuildNumber), the three values build.sh and the web Docker build produce -->\r\n<!-- Shuaib 2026-09-24\r\n - change: sign-in visibility checks now include jwtToken alongside keycloak and oktaAuth. -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the option key, singular since 2026-09-16, and on the sign-in\r\n branches, which know keycloak and oktaAuth but not jwtToken -->\r\n<div class=\"left-wrap sidemenu\" [class.collapsed]=\"isCollapsed\"\r\n [ngStyle]=\"{ 'z-index': !isCollapsed && isMobile ? 999999 : 'auto' }\"\r\n #sideMenu>\r\n <!-- Move toggle button OUTSIDE menu container -->\r\n <div class=\"side-toggle-handle\" id=\"toggle-id\" (click)=\"toggleCollapsed()\"\r\n [class.collapsed]=\"isCollapsed\">\r\n <span id=\"toggle-icon\" nz-icon\r\n [nzType]=\"isCollapsed ? 'caret-right' : 'caret-left'\"></span>\r\n </div>\r\n <div class=\"side-menu\" id=\"sidemenuwrap\"\r\n [ngStyle]=\"{ width: (isMobile && isCollapsed) ? '1px' : isCollapsed ? '60px' : '' }\">\r\n <!-- user content -->\r\n <ng-container *ngIf=\"loginedUserInfo && loginedUserInfo?.name\">\r\n <div class=\"user-profile\" [class.collapsed]=\"isCollapsed\">\r\n <img class=\"avatar\" [src]=\"loginedUserInfo?.image\" alt=\"User\" />\r\n <div [class]=\"isMobile ? '' : 'details'\" *ngIf=\"!isCollapsed\">\r\n <div class=\"name\">{{ loginedUserInfo.name }}</div>\r\n <div class=\"email\">{{ loginedUserInfo?.email }}</div>\r\n <div class=\"actions\" *ngIf=\"loginedUserInfo?.actions?.length\">\r\n <button *ngFor=\"let action of loginedUserInfo?.actions\" nz-button\r\n nzType=\"link\" nzSize=\"small\"\r\n (click)=\"invokeNavigate(action)\">\r\n <span>{{ action?.label?.caption }}</span>\r\n <span nz-icon [nzType]=\"action.icon\"></span>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-container>\r\n <!-- @contract decision: this container reads option (singular), like every other one\r\n reality: it read options (plural) until 2026-09-16. A menu file written for the old\r\n spelling is ignored without an error.\r\n\r\n @contract decision: sign-in visibility supports keycloak, oktaAuth and jwtToken.\r\n A JWT project uses the decoded user's login state, like the other providers.\r\n seen: the login type comparisons in the lines below -->\r\n <!-- dynamic menu -->\r\n <ul nz-menu nzMode=\"inline\" nzTheme=\"dark\"\r\n [nzInlineCollapsed]=\"isCollapsed\">\r\n <ng-container *ngFor=\"let item of menuItems\">\r\n <ng-container\r\n *ngIf=\"!item.submenu && item?.option?.action != 'logout'; else submenu\">\r\n <span\r\n [class]=\"environment?.login?.type === 'oktaAuth' || environment?.login?.type === 'keycloak' || environment?.login?.type === 'jwtToken' ? item.option?.login === true ? loginedUserInfo?.name ? '' : 'hidden' : item.option?.login === false ? loginedUserInfo?.name ? 'hidden' : '' : '' : userInfo?.userId ? item.option?.login ? item.option.role ? item.option.role == userInfo.userRole ? '' : 'hidden' : '' : 'hidden' : !item.option?.login ? '' : 'hidden' \">\r\n <li (click)=\"invokeNavigate(item)\" nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? item?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"item.icon\"></span>\r\n <span>{{ item?.label?.caption }}</span>\r\n </li>\r\n </span>\r\n </ng-container>\r\n <ng-template #submenu>\r\n <span\r\n [class]=\"item.option?.login === true && (environment?.login?.type === 'oktaAuth' || environment?.login?.type === 'keycloak' || environment?.login?.type === 'jwtToken') ? loginedUserInfo?.name ? '' : 'hidden' : item.option?.login === false ? loginedUserInfo?.name ? 'hidden' : '' : ''\">\r\n <li nz-submenu *ngIf=\"item?.option?.action != 'logout'\"\r\n [nzTitle]=\"item?.label?.caption\"\r\n [nzIcon]=\"item.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let child of item.submenu\">\r\n <li (click)=\"invokeNavigate(child)\" *ngIf=\"!child.submenu\"\r\n nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? child?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"child?.icon\"></span>\r\n <span>{{ child?.label?.caption }}</span>\r\n </li>\r\n <ng-container *ngIf=\"child.submenu\">\r\n <li nz-submenu [nzTitle]=\"child?.label?.caption\"\r\n [nzIcon]=\"child?.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let subChild of child.submenu\">\r\n <li (click)=\"invokeNavigate(subChild)\" nz-menu-item>\r\n <ng-container\r\n *ngIf=\"subChild?.option?.setImageUrl; else antIcon\">\r\n <img *ngIf=\"isUrl(subChild?.icon)\"\r\n [src]=\"subChild?.icon\" alt=\"icon\"\r\n style=\"width: 16px; height: 16px; margin-right: 8px;\" />\r\n </ng-container>\r\n <ng-template #antIcon>\r\n <span nz-icon [nzType]=\"subChild?.icon\"></span>\r\n </ng-template>\r\n <span>{{ subChild?.label?.caption }}</span>\r\n @if(subChild?.option?.numberedPagination &&\r\n !scrollPagination){\r\n <span nz-icon\r\n nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n @if(subChild?.option?.scrollPagination &&\r\n scrollPagination){\r\n <span nz-icon\r\n nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n @if(subChild?.option?.changeLang &&\r\n subChild?.option?.data_url === activeLanguage){\r\n <span nz-icon nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </span>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <!-- separator -->\r\n <li *ngIf=\"settingMenu.submenu\" class=\"menu-separator\"></li>\r\n\r\n <!-- settings menu -->\r\n <ng-container *ngIf=\"!settingMenu.submenu; else settingSubmenu\"\r\n style=\"position: absolute; bottom: 0;\">\r\n <li (click)=\"invokeNavigate(settingMenu)\" nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? settingMenu?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"settingMenu.icon\"></span>\r\n <span>{{ settingMenu?.label?.caption }}</span>\r\n </li>\r\n </ng-container>\r\n <ng-template #settingSubmenu>\r\n <li nz-submenu [nzTitle]=\"settingMenu?.label?.caption\"\r\n [nzIcon]=\"settingMenu.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let child of settingMenu.submenu\">\r\n <li (click)=\"invokeNavigate(child)\" *ngIf=\"!child.submenu\"\r\n nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? child?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"child?.icon\"></span>\r\n <span>{{ child?.label?.caption }}</span>\r\n </li>\r\n <ng-container *ngIf=\"child.submenu\">\r\n <li nz-submenu [nzTitle]=\"child?.label?.caption\"\r\n [nzIcon]=\"child?.icon\">\r\n <ul style=\"width: 260px;\">\r\n <ng-container\r\n *ngFor=\"let subChild of child.submenu; let i = index\">\r\n <li (click)=\"invokeNavigate(subChild, child)\"\r\n nz-menu-item>\r\n <span\r\n [class]=\"child?.label?.caption === 'Languages' ? '' : 'menu-row'\">\r\n <ng-container\r\n *ngIf=\"subChild?.option?.changeProject; else antIcon\">\r\n <img [src]=\"subChild?.icon\" alt=\"icon\"\r\n style=\"width: 16px; height: 16px; margin-right: 8px;\" />\r\n </ng-container>\r\n <ng-template #antIcon>\r\n <span nz-icon [nzType]=\"subChild?.icon\"></span>\r\n </ng-template>\r\n <span>{{ subChild?.label?.caption }}</span>\r\n <!-- Delete icon (prevent click bubbling) -->\r\n <span *ngIf=\"child?.label?.caption != 'Languages'\"\r\n nz-icon nzType=\"delete\" class=\"delete-icon\"\r\n style=\"padding-left: 5%;\"\r\n (click)=\"deleteMenuItem(subChild, i, $event)\"></span>\r\n </span>\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-template>\r\n </ul>\r\n @if (!isCollapsed && appVersionInfo?.appVersion) {\r\n <div class=\"app-version-footer\">\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Version</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appVersion }}</span>\r\n </div>\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Build</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appBuild || '-' }}</span>\r\n </div>\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Build No.</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appBuildNumber || '-' }}</span>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{position:relative;display:block;flex:0 0 auto;width:250px;height:100%;min-height:0;overflow:visible;transition:width .3s ease}.left-wrap.sidemenu{position:relative;width:250px;height:100%;min-height:0;overflow:visible;transition:width .3s ease,transform .3s ease}.side-menu{position:relative;display:flex;flex-direction:column;width:250px;height:100%;min-height:0;color:#fff;background-color:#0d1b2a;overflow:hidden;transition:width .3s ease,box-shadow .3s ease}:host:has(.left-wrap.sidemenu.collapsed){width:64px}.left-wrap.sidemenu.collapsed,.left-wrap.sidemenu.collapsed .side-menu{width:64px}.menu-separator{flex:0 0 auto;height:1px;margin:15px 12px 6px;background-color:#ffffff26;border:0;list-style:none}.user-profile{flex:0 0 auto;padding:20px 10px 16px;margin-top:40px;text-align:center;border-bottom:1px solid rgba(255,255,255,.1);overflow:hidden;transition:padding .25s ease,margin .25s ease,opacity .25s ease,max-height .25s ease}.user-profile .avatar{display:block;width:64px;height:64px;margin:0 auto 8px;object-fit:cover;border:2px solid #ffffff;border-radius:50%;transition:width .25s ease,height .25s ease,margin .25s ease}.user-profile .details{min-width:0;overflow:hidden;transition:opacity .2s ease}.user-profile .name{color:#fff;font-size:14px;font-weight:600;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.user-profile .email{margin:2px 0 8px;color:#aaa;font-size:12px;line-height:1.4;overflow-wrap:anywhere;word-break:break-word}.user-profile .actions{display:flex;align-items:center;justify-content:center;gap:6px}.user-profile .actions button{margin:0;color:#fff}.left-wrap.sidemenu.collapsed .user-profile{padding:12px 8px}.left-wrap.sidemenu.collapsed .user-profile .avatar{width:40px;height:40px;margin-bottom:0}.left-wrap.sidemenu.collapsed .user-profile .details,.left-wrap.sidemenu.collapsed .user-profile .name,.left-wrap.sidemenu.collapsed .user-profile .email,.left-wrap.sidemenu.collapsed .user-profile .actions{display:none}.side-menu ul[nz-menu]{flex:1 1 auto;width:100%;min-height:0;margin:0;padding:4px 0 8px;overflow-x:hidden;overflow-y:auto;background-color:transparent;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.28) transparent;scroll-behavior:smooth}.side-menu ul[nz-menu]::-webkit-scrollbar{width:6px}.side-menu ul[nz-menu]::-webkit-scrollbar-track{background:transparent}.side-menu ul[nz-menu]::-webkit-scrollbar-thumb{background-color:#ffffff47;border-radius:999px}.side-menu ul[nz-menu]::-webkit-scrollbar-thumb:hover{background-color:#ffffff6b}.menu-row{display:flex;align-items:center;width:100%;min-width:0}.menu-text{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.delete-icon{flex:0 0 auto;margin-left:auto;color:#ffffffb3;cursor:pointer;transition:color .18s ease,transform .18s ease}.delete-icon:hover{color:#ff7875;transform:scale(1.08)}.projects{position:static;flex:0 0 auto;width:100%;margin-top:auto;background-color:#0d1b2a}.side-toggle-handle{position:absolute;top:24px;right:-14px;z-index:10000;display:flex;align-items:center;justify-content:center;width:28px;height:48px;padding:0;color:#fff;background-color:#0d1b2a;border:0;border-radius:0 6px 6px 0;box-shadow:2px 0 6px #00000040;cursor:pointer;transform:translateY(-50%);transition:background-color .2s ease,box-shadow .2s ease}.side-toggle-handle:hover{background-color:#142a40;box-shadow:3px 0 9px #0000004d}.side-toggle-handle:focus-visible{outline:3px solid rgba(24,144,255,.45);outline-offset:2px}.side-toggle-handle span{color:#fff;font-size:14px;line-height:1;transition:transform .25s ease}.collapse-btn{position:absolute;top:8px;right:8px;z-index:10001}:host(:not(.sidemenu-mobile)) .left-wrap.sidemenu.collapsed:hover{transform:translate(0)}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed{width:64px!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-item,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-submenu>.ant-menu-submenu-title{display:flex!important;align-items:center!important;justify-content:center!important;width:45px!important;height:32px!important;margin-inline:0!important;padding-inline:0!important;overflow:hidden!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-title-content,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .menu-text,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .delete-icon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-submenu-arrow{display:none!important;width:0!important;min-width:0!important;max-width:0!important;margin:0!important;padding:0!important;visibility:hidden!important;overflow:hidden!important;text-overflow:clip!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-item-icon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item>.anticon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title>.anticon{display:inline-flex!important;flex:0 0 auto;align-items:center;justify-content:center;width:auto!important;min-width:18px;margin:0!important;padding:0!important;color:inherit;font-size:18px;line-height:1;visibility:visible!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .menu-row{display:contents}:host(.sidemenu-mobile){position:absolute;top:0;bottom:0;left:0;z-index:9999;width:250px;height:100%}:host(.sidemenu-mobile):has(.left-wrap.sidemenu.collapsed){width:0}:host(.sidemenu-mobile) .left-wrap.sidemenu{width:250px;height:100%}:host(.sidemenu-mobile) .side-menu,:host(.sidemenu-mobile) .side-menu.open{width:250px;height:100%;box-shadow:8px 0 22px #0000003d}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed{width:250px;transform:translate(-250px)}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .side-menu{width:250px;box-shadow:none}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .side-toggle-handle{right:-28px}:host(.sidemenu-mobile) .side-toggle-handle{top:0;right:0;padding:0;transform:none!important}:host(.sidemenu-mobile) .collapse-btn{top:8px;right:8px}:host(.sidemenu-mobile) .user-profile{margin-top:40px}:host(.sidemenu-mobile) .user-profile .avatar{width:48px;height:48px}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .user-profile{max-height:0;padding:0;margin:0;opacity:0;overflow:hidden;border-bottom:0}@media(prefers-reduced-motion:reduce){:host,.left-wrap.sidemenu,.side-menu,.user-profile,.user-profile .avatar,.side-toggle-handle,.side-toggle-handle span{transition:none}.side-menu ul[nz-menu]{scroll-behavior:auto}}.app-version-footer{flex-shrink:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:10px 12px;border-top:1px solid rgba(255,255,255,.15);color:#ffffffa6;font-size:11px;line-height:1.4;text-align:center}.app-version-footer__item{display:flex;flex-direction:column;min-width:0}.app-version-footer__label{font-size:10px;opacity:.7}.app-version-footer__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pagination-check{position:absolute;top:50%;right:42px;transform:translateY(-50%)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: PlatformModule }, { kind: "ngmodule", type:
8559
8094
  // NG-ZORRO
8560
8095
  NzMenuModule }, { kind: "directive", type: i11$1.NzMenuDirective, selector: "[nz-menu]", inputs: ["nzInlineIndent", "nzTheme", "nzMode", "nzInlineCollapsed", "nzSelectable"], outputs: ["nzClick"], exportAs: ["nzMenu"] }, { kind: "component", type: i11$1.NzMenuItemComponent, selector: "[nz-menu-item]", inputs: ["nzPaddingLeft", "nzDisabled", "nzSelected", "nzDanger", "nzMatchRouterExact", "nzMatchRouter"], exportAs: ["nzMenuItem"] }, { kind: "component", type: i11$1.NzSubMenuComponent, selector: "[nz-submenu]", inputs: ["nzMenuClassName", "nzPaddingLeft", "nzTitle", "nzIcon", "nzTriggerSubMenuAction", "nzOpen", "nzDisabled", "nzPlacement"], outputs: ["nzOpenChange"], exportAs: ["nzSubmenu"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: NzTooltipModule }, { kind: "directive", type: i13$1.NzTooltipDirective, selector: "[nz-tooltip]", inputs: ["nzTooltipTitle", "nzTooltipTitleContext", "nz-tooltip", "nzTooltipTrigger", "nzTooltipPlacement", "nzTooltipOrigin", "nzTooltipVisible", "nzTooltipMouseEnterDelay", "nzTooltipMouseLeaveDelay", "nzTooltipOverlayClassName", "nzTooltipOverlayStyle", "nzTooltipArrowPointAtCenter", "cdkConnectedOverlayPush", "nzTooltipColor"], outputs: ["nzTooltipVisibleChange"], exportAs: ["nzTooltip"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i13.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i14.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "ngmodule", type:
8561
8096
  // PrimeNG
@@ -8574,7 +8109,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
8574
8109
  // PrimeNG
8575
8110
  DynamicDialogModule
8576
8111
  ], template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - new: the active language has a check mark, like the active pagination type\r\n - fix: all check marks (pagination, language) stand in one column at the right edge; the\r\n pagination marks followed their caption at a fixed distance and landed elsewhere on iOS\r\n - change: the version footer has three columns - version, build (source commit) and build\r\n number (appBuildNumber), the three values build.sh and the web Docker build produce -->\r\n<!-- Shuaib 2026-09-24\r\n - change: sign-in visibility checks now include jwtToken alongside keycloak and oktaAuth. -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the option key, singular since 2026-09-16, and on the sign-in\r\n branches, which know keycloak and oktaAuth but not jwtToken -->\r\n<div class=\"left-wrap sidemenu\" [class.collapsed]=\"isCollapsed\"\r\n [ngStyle]=\"{ 'z-index': !isCollapsed && isMobile ? 999999 : 'auto' }\"\r\n #sideMenu>\r\n <!-- Move toggle button OUTSIDE menu container -->\r\n <div class=\"side-toggle-handle\" id=\"toggle-id\" (click)=\"toggleCollapsed()\"\r\n [class.collapsed]=\"isCollapsed\">\r\n <span id=\"toggle-icon\" nz-icon\r\n [nzType]=\"isCollapsed ? 'caret-right' : 'caret-left'\"></span>\r\n </div>\r\n <div class=\"side-menu\" id=\"sidemenuwrap\"\r\n [ngStyle]=\"{ width: (isMobile && isCollapsed) ? '1px' : isCollapsed ? '60px' : '' }\">\r\n <!-- user content -->\r\n <ng-container *ngIf=\"loginedUserInfo && loginedUserInfo?.name\">\r\n <div class=\"user-profile\" [class.collapsed]=\"isCollapsed\">\r\n <img class=\"avatar\" [src]=\"loginedUserInfo?.image\" alt=\"User\" />\r\n <div [class]=\"isMobile ? '' : 'details'\" *ngIf=\"!isCollapsed\">\r\n <div class=\"name\">{{ loginedUserInfo.name }}</div>\r\n <div class=\"email\">{{ loginedUserInfo?.email }}</div>\r\n <div class=\"actions\" *ngIf=\"loginedUserInfo?.actions?.length\">\r\n <button *ngFor=\"let action of loginedUserInfo?.actions\" nz-button\r\n nzType=\"link\" nzSize=\"small\"\r\n (click)=\"invokeNavigate(action)\">\r\n <span>{{ action?.label?.caption }}</span>\r\n <span nz-icon [nzType]=\"action.icon\"></span>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-container>\r\n <!-- @contract decision: this container reads option (singular), like every other one\r\n reality: it read options (plural) until 2026-09-16. A menu file written for the old\r\n spelling is ignored without an error.\r\n\r\n @contract decision: sign-in visibility supports keycloak, oktaAuth and jwtToken.\r\n A JWT project uses the decoded user's login state, like the other providers.\r\n seen: the login type comparisons in the lines below -->\r\n <!-- dynamic menu -->\r\n <ul nz-menu nzMode=\"inline\" nzTheme=\"dark\"\r\n [nzInlineCollapsed]=\"isCollapsed\">\r\n <ng-container *ngFor=\"let item of menuItems\">\r\n <ng-container\r\n *ngIf=\"!item.submenu && item?.option?.action != 'logout'; else submenu\">\r\n <span\r\n [class]=\"environment?.login?.type === 'oktaAuth' || environment?.login?.type === 'keycloak' || environment?.login?.type === 'jwtToken' ? item.option?.login === true ? loginedUserInfo?.name ? '' : 'hidden' : item.option?.login === false ? loginedUserInfo?.name ? 'hidden' : '' : '' : userInfo?.userId ? item.option?.login ? item.option.role ? item.option.role == userInfo.userRole ? '' : 'hidden' : '' : 'hidden' : !item.option?.login ? '' : 'hidden' \">\r\n <li (click)=\"invokeNavigate(item)\" nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? item?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"item.icon\"></span>\r\n <span>{{ item?.label?.caption }}</span>\r\n </li>\r\n </span>\r\n </ng-container>\r\n <ng-template #submenu>\r\n <span\r\n [class]=\"item.option?.login === true && (environment?.login?.type === 'oktaAuth' || environment?.login?.type === 'keycloak' || environment?.login?.type === 'jwtToken') ? loginedUserInfo?.name ? '' : 'hidden' : item.option?.login === false ? loginedUserInfo?.name ? 'hidden' : '' : ''\">\r\n <li nz-submenu *ngIf=\"item?.option?.action != 'logout'\"\r\n [nzTitle]=\"item?.label?.caption\"\r\n [nzIcon]=\"item.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let child of item.submenu\">\r\n <li (click)=\"invokeNavigate(child)\" *ngIf=\"!child.submenu\"\r\n nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? child?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"child?.icon\"></span>\r\n <span>{{ child?.label?.caption }}</span>\r\n </li>\r\n <ng-container *ngIf=\"child.submenu\">\r\n <li nz-submenu [nzTitle]=\"child?.label?.caption\"\r\n [nzIcon]=\"child?.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let subChild of child.submenu\">\r\n <li (click)=\"invokeNavigate(subChild)\" nz-menu-item>\r\n <ng-container\r\n *ngIf=\"subChild?.option?.setImageUrl; else antIcon\">\r\n <img *ngIf=\"isUrl(subChild?.icon)\"\r\n [src]=\"subChild?.icon\" alt=\"icon\"\r\n style=\"width: 16px; height: 16px; margin-right: 8px;\" />\r\n </ng-container>\r\n <ng-template #antIcon>\r\n <span nz-icon [nzType]=\"subChild?.icon\"></span>\r\n </ng-template>\r\n <span>{{ subChild?.label?.caption }}</span>\r\n @if(subChild?.option?.numberedPagination &&\r\n !scrollPagination){\r\n <span nz-icon\r\n nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n @if(subChild?.option?.scrollPagination &&\r\n scrollPagination){\r\n <span nz-icon\r\n nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n @if(subChild?.option?.changeLang &&\r\n subChild?.option?.data_url === activeLanguage){\r\n <span nz-icon nzType=\"check\" nzTheme=\"outline\"\r\n class=\"pagination-check\">\r\n </span>\r\n }\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </span>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <!-- separator -->\r\n <li *ngIf=\"settingMenu.submenu\" class=\"menu-separator\"></li>\r\n\r\n <!-- settings menu -->\r\n <ng-container *ngIf=\"!settingMenu.submenu; else settingSubmenu\"\r\n style=\"position: absolute; bottom: 0;\">\r\n <li (click)=\"invokeNavigate(settingMenu)\" nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? settingMenu?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"settingMenu.icon\"></span>\r\n <span>{{ settingMenu?.label?.caption }}</span>\r\n </li>\r\n </ng-container>\r\n <ng-template #settingSubmenu>\r\n <li nz-submenu [nzTitle]=\"settingMenu?.label?.caption\"\r\n [nzIcon]=\"settingMenu.icon\">\r\n <ul>\r\n <ng-container *ngFor=\"let child of settingMenu.submenu\">\r\n <li (click)=\"invokeNavigate(child)\" *ngIf=\"!child.submenu\"\r\n nz-menu-item nz-tooltip\r\n nzTooltipPlacement=\"right\"\r\n [nzTooltipTitle]=\"isCollapsed && !isMobile ? child?.label?.caption : ''\">\r\n <span nz-icon [nzType]=\"child?.icon\"></span>\r\n <span>{{ child?.label?.caption }}</span>\r\n </li>\r\n <ng-container *ngIf=\"child.submenu\">\r\n <li nz-submenu [nzTitle]=\"child?.label?.caption\"\r\n [nzIcon]=\"child?.icon\">\r\n <ul style=\"width: 260px;\">\r\n <ng-container\r\n *ngFor=\"let subChild of child.submenu; let i = index\">\r\n <li (click)=\"invokeNavigate(subChild, child)\"\r\n nz-menu-item>\r\n <span\r\n [class]=\"child?.label?.caption === 'Languages' ? '' : 'menu-row'\">\r\n <ng-container\r\n *ngIf=\"subChild?.option?.changeProject; else antIcon\">\r\n <img [src]=\"subChild?.icon\" alt=\"icon\"\r\n style=\"width: 16px; height: 16px; margin-right: 8px;\" />\r\n </ng-container>\r\n <ng-template #antIcon>\r\n <span nz-icon [nzType]=\"subChild?.icon\"></span>\r\n </ng-template>\r\n <span>{{ subChild?.label?.caption }}</span>\r\n <!-- Delete icon (prevent click bubbling) -->\r\n <span *ngIf=\"child?.label?.caption != 'Languages'\"\r\n nz-icon nzType=\"delete\" class=\"delete-icon\"\r\n style=\"padding-left: 5%;\"\r\n (click)=\"deleteMenuItem(subChild, i, $event)\"></span>\r\n </span>\r\n </li>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-container>\r\n </ng-container>\r\n </ul>\r\n </li>\r\n </ng-template>\r\n </ul>\r\n @if (!isCollapsed && appVersionInfo?.appVersion) {\r\n <div class=\"app-version-footer\">\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Version</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appVersion }}</span>\r\n </div>\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Build</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appBuild || '-' }}</span>\r\n </div>\r\n <div class=\"app-version-footer__item\">\r\n <span class=\"app-version-footer__label\">Build No.</span>\r\n <span class=\"app-version-footer__value\">{{ appVersionInfo.appBuildNumber || '-' }}</span>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{position:relative;display:block;flex:0 0 auto;width:250px;height:100%;min-height:0;overflow:visible;transition:width .3s ease}.left-wrap.sidemenu{position:relative;width:250px;height:100%;min-height:0;overflow:visible;transition:width .3s ease,transform .3s ease}.side-menu{position:relative;display:flex;flex-direction:column;width:250px;height:100%;min-height:0;color:#fff;background-color:#0d1b2a;overflow:hidden;transition:width .3s ease,box-shadow .3s ease}:host:has(.left-wrap.sidemenu.collapsed){width:64px}.left-wrap.sidemenu.collapsed,.left-wrap.sidemenu.collapsed .side-menu{width:64px}.menu-separator{flex:0 0 auto;height:1px;margin:15px 12px 6px;background-color:#ffffff26;border:0;list-style:none}.user-profile{flex:0 0 auto;padding:20px 10px 16px;margin-top:40px;text-align:center;border-bottom:1px solid rgba(255,255,255,.1);overflow:hidden;transition:padding .25s ease,margin .25s ease,opacity .25s ease,max-height .25s ease}.user-profile .avatar{display:block;width:64px;height:64px;margin:0 auto 8px;object-fit:cover;border:2px solid #ffffff;border-radius:50%;transition:width .25s ease,height .25s ease,margin .25s ease}.user-profile .details{min-width:0;overflow:hidden;transition:opacity .2s ease}.user-profile .name{color:#fff;font-size:14px;font-weight:600;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.user-profile .email{margin:2px 0 8px;color:#aaa;font-size:12px;line-height:1.4;overflow-wrap:anywhere;word-break:break-word}.user-profile .actions{display:flex;align-items:center;justify-content:center;gap:6px}.user-profile .actions button{margin:0;color:#fff}.left-wrap.sidemenu.collapsed .user-profile{padding:12px 8px}.left-wrap.sidemenu.collapsed .user-profile .avatar{width:40px;height:40px;margin-bottom:0}.left-wrap.sidemenu.collapsed .user-profile .details,.left-wrap.sidemenu.collapsed .user-profile .name,.left-wrap.sidemenu.collapsed .user-profile .email,.left-wrap.sidemenu.collapsed .user-profile .actions{display:none}.side-menu ul[nz-menu]{flex:1 1 auto;width:100%;min-height:0;margin:0;padding:4px 0 8px;overflow-x:hidden;overflow-y:auto;background-color:transparent;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.28) transparent;scroll-behavior:smooth}.side-menu ul[nz-menu]::-webkit-scrollbar{width:6px}.side-menu ul[nz-menu]::-webkit-scrollbar-track{background:transparent}.side-menu ul[nz-menu]::-webkit-scrollbar-thumb{background-color:#ffffff47;border-radius:999px}.side-menu ul[nz-menu]::-webkit-scrollbar-thumb:hover{background-color:#ffffff6b}.menu-row{display:flex;align-items:center;width:100%;min-width:0}.menu-text{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.delete-icon{flex:0 0 auto;margin-left:auto;color:#ffffffb3;cursor:pointer;transition:color .18s ease,transform .18s ease}.delete-icon:hover{color:#ff7875;transform:scale(1.08)}.projects{position:static;flex:0 0 auto;width:100%;margin-top:auto;background-color:#0d1b2a}.side-toggle-handle{position:absolute;top:24px;right:-14px;z-index:10000;display:flex;align-items:center;justify-content:center;width:28px;height:48px;padding:0;color:#fff;background-color:#0d1b2a;border:0;border-radius:0 6px 6px 0;box-shadow:2px 0 6px #00000040;cursor:pointer;transform:translateY(-50%);transition:background-color .2s ease,box-shadow .2s ease}.side-toggle-handle:hover{background-color:#142a40;box-shadow:3px 0 9px #0000004d}.side-toggle-handle:focus-visible{outline:3px solid rgba(24,144,255,.45);outline-offset:2px}.side-toggle-handle span{color:#fff;font-size:14px;line-height:1;transition:transform .25s ease}.collapse-btn{position:absolute;top:8px;right:8px;z-index:10001}:host(:not(.sidemenu-mobile)) .left-wrap.sidemenu.collapsed:hover{transform:translate(0)}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed{width:64px!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-item,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item-group>.ant-menu-item-group-list>.ant-menu-submenu>.ant-menu-submenu-title{display:flex!important;align-items:center!important;justify-content:center!important;width:45px!important;height:32px!important;margin-inline:0!important;padding-inline:0!important;overflow:hidden!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-title-content,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .menu-text,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .delete-icon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-submenu-arrow{display:none!important;width:0!important;min-width:0!important;max-width:0!important;margin:0!important;padding:0!important;visibility:hidden!important;overflow:hidden!important;text-overflow:clip!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .ant-menu-item-icon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-item>.anticon,:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed>.ant-menu-submenu>.ant-menu-submenu-title>.anticon{display:inline-flex!important;flex:0 0 auto;align-items:center;justify-content:center;width:auto!important;min-width:18px;margin:0!important;padding:0!important;color:inherit;font-size:18px;line-height:1;visibility:visible!important}:host(:not(.sidemenu-mobile)) ::ng-deep .ant-menu.ant-menu-inline-collapsed .menu-row{display:contents}:host(.sidemenu-mobile){position:absolute;top:0;bottom:0;left:0;z-index:9999;width:250px;height:100%}:host(.sidemenu-mobile):has(.left-wrap.sidemenu.collapsed){width:0}:host(.sidemenu-mobile) .left-wrap.sidemenu{width:250px;height:100%}:host(.sidemenu-mobile) .side-menu,:host(.sidemenu-mobile) .side-menu.open{width:250px;height:100%;box-shadow:8px 0 22px #0000003d}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed{width:250px;transform:translate(-250px)}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .side-menu{width:250px;box-shadow:none}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .side-toggle-handle{right:-28px}:host(.sidemenu-mobile) .side-toggle-handle{top:0;right:0;padding:0;transform:none!important}:host(.sidemenu-mobile) .collapse-btn{top:8px;right:8px}:host(.sidemenu-mobile) .user-profile{margin-top:40px}:host(.sidemenu-mobile) .user-profile .avatar{width:48px;height:48px}:host(.sidemenu-mobile) .left-wrap.sidemenu.collapsed .user-profile{max-height:0;padding:0;margin:0;opacity:0;overflow:hidden;border-bottom:0}@media(prefers-reduced-motion:reduce){:host,.left-wrap.sidemenu,.side-menu,.user-profile,.user-profile .avatar,.side-toggle-handle,.side-toggle-handle span{transition:none}.side-menu ul[nz-menu]{scroll-behavior:auto}}.app-version-footer{flex-shrink:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:10px 12px;border-top:1px solid rgba(255,255,255,.15);color:#ffffffa6;font-size:11px;line-height:1.4;text-align:center}.app-version-footer__item{display:flex;flex-direction:column;min-width:0}.app-version-footer__label{font-size:10px;opacity:.7}.app-version-footer__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pagination-check{position:absolute;top:50%;right:42px;transform:translateY(-50%)}\n"] }]
8577
- }], ctorParameters: () => [{ type: i1$5.NzI18nService }, { type: MobileKeycloakService }, { type: Couchdb }, { type: i7.OAuthService }, { type: i1.Platform }, { type: RequestService }, { type: UtilityService }, { type: i1$4.DialogService }, { type: NotificationService }, { type: i0.Injector }], propDecorators: { menuItems: [{
8112
+ }], ctorParameters: () => [{ type: i1$4.NzI18nService }, { type: MobileKeycloakService }, { type: Couchdb }, { type: i7.OAuthService }, { type: i1.Platform }, { type: RequestService }, { type: UtilityService }, { type: i1$3.DialogService }, { type: NotificationService }, { type: i0.Injector }], propDecorators: { menuItems: [{
8578
8113
  type: Input
8579
8114
  }], userInfo: [{
8580
8115
  type: Input
@@ -9006,6 +8541,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
9006
8541
  args: [{ selector: 'vl-custom-keyboard', standalone: true, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13\r\n - change: the space key has no label (it said \"Space\" in every language); a label per language\r\n would have to be maintained for each new project language -->\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - new: a ?123 / ABC key switches string keyboards between letters and the symbol page -->\r\n@if (keyboard.activeSession(); as session) {\r\n<section class=\"custom-keyboard\" data-vl-custom-keyboard [class.custom-keyboard--numeric]=\"\r\n session.type === 'numeric' ||\r\n session.type === 'date' ||\r\n session.type === 'time'\" [class.custom-keyboard--opening]=\"keyboard.interactionBlocked()\"\r\n [attr.aria-label]=\"session.type + ' keyboard'\" (pointerdown)=\"$event.preventDefault()\">\r\n\r\n @for (row of rows; track $index) {\r\n <div class=\"custom-keyboard__row\">\r\n @for (key of row; track key) {\r\n <button type=\"button\" class=\"custom-keyboard__key\" (click)=\"enterKey(key)\">\r\n {{ key }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- One mobile-style action row for every keyboard type -->\r\n <div class=\"custom-keyboard__actions\" [class.custom-keyboard__actions--compact]=\"\r\n session.type !== 'alpha' &&\r\n session.type !== 'string'\r\n \" [class.custom-keyboard__actions--symbols]=\"hasSymbolPage\">\r\n\r\n @if (\r\n session.type === 'alpha' ||\r\n session.type === 'string'\r\n ) {\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__shift\" [class.active]=\"uppercase()\"\r\n [disabled]=\"symbols()\" aria-label=\"Change letter case\" title=\"Shift\" (click)=\"toggleCase()\">\r\n <i class=\"pi pi-arrow-up\"></i>\r\n </button>\r\n\r\n @if (hasSymbolPage) {\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__page\"\r\n [attr.aria-label]=\"symbols() ? 'Letters' : 'Digits and symbols'\" (click)=\"toggleSymbols()\">\r\n {{ symbols() ? 'ABC' : '?123' }}\r\n </button>\r\n }\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__space\" aria-label=\"Space\" (click)=\"enterSpace()\">\r\n </button>\r\n }\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__delete\"\r\n aria-label=\"Delete previous character\" title=\"Delete\" (click)=\"keyboard.backspace()\">\r\n <i class=\"pi pi-delete-left\"></i>\r\n </button>\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__done\" aria-label=\"Done\"\r\n title=\"Done\" (click)=\"keyboard.close()\">\r\n <i class=\"pi pi-check\"></i>\r\n </button>\r\n </div>\r\n</section>\r\n}\r\n", styles: [":host{display:contents}.custom-keyboard{position:fixed;right:0;bottom:0;left:0;z-index:200000;display:block;width:100%;max-height:min(52dvh,390px);padding:8px 6px calc(8px + env(safe-area-inset-bottom,0px));background:var(--vl-keyboard-background, var(--vl-page-background, #ffffff));box-shadow:0 -6px 20px #0f172a38;box-sizing:border-box;overflow-x:hidden;overflow-y:auto;user-select:none;-webkit-user-select:none;touch-action:manipulation;-webkit-overflow-scrolling:touch}.custom-keyboard__row{display:flex;justify-content:center;gap:4px;width:100%;margin-bottom:6px}.custom-keyboard__key{display:flex;flex:1 1 0;width:auto;max-width:52px;min-width:0;height:42px;align-items:center;justify-content:center;padding:0;color:#111827;font-size:16px;font-weight:700;line-height:1;background:#fff;border:1px solid #d1d5db;border-radius:5px;box-shadow:0 1px 2px #0f172a47;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__key:active{background:#e6f4ff;transform:scale(.96)}.custom-keyboard--numeric .custom-keyboard__key{max-width:92px;height:46px;font-size:18px}.custom-keyboard__actions{display:grid;grid-template-columns:46px minmax(90px,1fr) 46px 54px;gap:6px;width:100%;margin-top:4px;padding:0 4px}.custom-keyboard__actions--compact{grid-template-columns:minmax(52px,1fr) minmax(52px,1fr)}.custom-keyboard__actions--symbols{grid-template-columns:46px 54px minmax(70px,1fr) 46px 54px}.custom-keyboard__action-button{display:flex;width:100%;min-width:0;height:42px;align-items:center;justify-content:center;padding:0 8px;color:#172033;font-size:15px;font-weight:700;line-height:1;border:0;border-radius:6px;box-shadow:0 1px 2px #0f172a40;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__action-button i{font-size:17px;line-height:1;pointer-events:none}.custom-keyboard__action-button:active{transform:scale(.96)}.custom-keyboard__shift{color:#172033;background:#aeb6c2}.custom-keyboard__shift.active{color:#fff;background:#64748b}.custom-keyboard__space{color:#172033;background:#fff}.custom-keyboard__delete{color:#172033;background:#aeb6c2}.custom-keyboard__done{color:#fff;background:#1890ff}.custom-keyboard__page{color:#172033;font-size:14px;background:#aeb6c2}.custom-keyboard__shift:disabled{opacity:.45}.custom-keyboard__key:focus-visible,.custom-keyboard__action-button:focus-visible{outline:3px solid rgba(24,144,255,.35);outline-offset:1px}@media(max-height:650px){.custom-keyboard{max-height:58dvh;padding-top:6px}.custom-keyboard__row{gap:3px;margin-bottom:4px}.custom-keyboard__key{height:35px;font-size:14px}.custom-keyboard--numeric .custom-keyboard__key{height:38px;font-size:16px}.custom-keyboard__action-button{height:36px}}@media(max-width:360px){.custom-keyboard{padding-right:4px;padding-left:4px}.custom-keyboard__row{gap:3px}.custom-keyboard__key{font-size:14px}.custom-keyboard__actions{grid-template-columns:42px minmax(70px,1fr) 42px 48px;gap:4px;padding-right:2px;padding-left:2px}.custom-keyboard__actions--compact{grid-template-columns:minmax(48px,1fr) minmax(48px,1fr)}.custom-keyboard__actions--symbols{grid-template-columns:40px 48px minmax(56px,1fr) 40px 46px}}.custom-keyboard--opening .custom-keyboard__key,.custom-keyboard--opening .custom-keyboard__action-button{pointer-events:none}.custom-keyboard--opening{cursor:default}::ng-deep .picker-field{position:relative}::ng-deep .picker-field__icon{position:absolute;right:0;top:0;height:100%;width:2.5rem;border:0;background:transparent;cursor:pointer}::ng-deep .picker-field--active input{border-color:var(--modal-field-focus-color, var(--p-primary-color, #10b981))!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow, 0 0 0 2px rgba(16, 185, 129, .16))!important}::ng-deep .p-datepicker-panel.model-datepicker-panel--docked,::ng-deep .model-datepicker-panel--docked{position:fixed!important;background:var(--vl-keyboard-background, var(--vl-page-background, #ffffff))!important;inset:auto 0 0!important;width:100%!important;min-width:100%!important;max-width:100%!important;max-height:min(52dvh,420px)!important;margin:0!important;padding-bottom:env(safe-area-inset-bottom,0px)!important;overflow-y:auto!important;transform:none!important;border-radius:12px 12px 0 0!important;box-shadow:0 -6px 20px #0f172a38!important;box-sizing:border-box!important}::ng-deep .model-timepicker-panel.p-datepicker-panel:not(.model-datepicker-panel--docked){width:auto!important;min-width:0!important;padding:4px 8px!important;border-radius:8px!important}::ng-deep .model-timepicker-panel .p-datepicker-time-picker{gap:4px!important;padding:0!important;border:0!important}::ng-deep .model-timepicker-panel .p-datepicker-hour-picker,::ng-deep .model-timepicker-panel .p-datepicker-minute-picker{gap:0!important}::ng-deep .model-timepicker-panel .p-datepicker-hour-picker span,::ng-deep .model-timepicker-panel .p-datepicker-minute-picker span,::ng-deep .model-timepicker-panel .p-datepicker-separator{font-size:1rem!important;line-height:1.2!important}::ng-deep .model-timepicker-panel .p-datepicker-increment-button,::ng-deep .model-timepicker-panel .p-datepicker-decrement-button{width:2rem!important;height:1.75rem!important}::ng-deep body.vl-side-dock .model-datepicker-panel--docked{inset:0 0 0 auto!important;width:var(--vl-side-dock-width, 50vw)!important;min-width:0!important;max-width:var(--vl-side-dock-width, 50vw)!important;height:100dvh!important;max-height:100dvh!important;min-height:0!important;padding-top:env(safe-area-inset-top,0px)!important;padding-right:env(safe-area-inset-right,0px)!important;border-left:1px solid #e5e7eb!important;box-shadow:-6px 0 20px #0f172a2e!important}.custom-keyboard__key{height:clamp(26px,5.2dvh,42px)}.custom-keyboard--numeric .custom-keyboard__key{height:clamp(28px,5.6dvh,46px)}.custom-keyboard__action-button{height:clamp(28px,5.2dvh,42px)}.custom-keyboard__row{margin-bottom:clamp(3px,.8dvh,6px)}::ng-deep body.vl-side-dock .custom-keyboard{inset:0 0 0 auto;width:var(--vl-side-dock-width, 50vw);max-height:none;height:100dvh;display:flex;flex-direction:column;justify-content:flex-start;padding-top:env(safe-area-inset-top,0px);padding-right:max(6px,env(safe-area-inset-right,0px));box-shadow:-6px 0 20px #0f172a2e}.custom-keyboard,::ng-deep .model-datepicker-panel--docked{border-top-left-radius:var(--p-dialog-border-radius, 12px)!important;border-top-right-radius:var(--p-dialog-border-radius, 12px)!important;overflow:hidden}::ng-deep .model-datepicker-panel--docked .p-datepicker-header{padding-top:clamp(2px,.6dvh,8px)!important;padding-bottom:clamp(2px,.6dvh,8px)!important}::ng-deep .model-datepicker-panel--docked .p-datepicker-day-cell,::ng-deep .model-datepicker-panel--docked .p-datepicker-weekday-cell{padding:1px!important}::ng-deep .model-datepicker-panel--docked .p-datepicker-day{width:var(--vl-day-size, clamp(28px, 4.4dvh, 40px))!important;height:var(--vl-day-size, clamp(28px, 4.4dvh, 40px))!important}::ng-deep body.vl-side-dock .model-datepicker-panel--docked,::ng-deep body.vl-side-dock .custom-keyboard{top:var(--vl-side-dock-top, max(8px, env(safe-area-inset-top, 0px)))!important;bottom:var(--vl-side-dock-bottom, max(8px, env(safe-area-inset-bottom, 0px)))!important;right:max(8px,env(safe-area-inset-right,0px))!important;left:auto!important;width:calc(var(--vl-side-dock-width, 50vw) - max(8px,env(safe-area-inset-right,0px)))!important;max-width:none!important;height:auto!important;max-height:none!important;padding-top:6px!important;padding-right:6px!important;border-radius:var(--p-dialog-border-radius, 12px)!important;overflow:hidden!important;box-shadow:var(--p-dialog-shadow, 0 20px 25px -5px rgba(0, 0, 0, .1), 0 8px 10px -6px rgba(0, 0, 0, .1))!important}::ng-deep body.vl-custom-keyboard-open .custom-keyboard,::ng-deep body.vl-custom-keyboard-open .model-datepicker-panel--docked{left:var(--vl-dock-left, 0px)!important;right:var(--vl-dock-right, 0px)!important;width:auto!important;min-width:0!important;max-width:none!important;bottom:var(--vl-dock-bottom-gap)!important;padding-bottom:8px!important;border-radius:var(--p-dialog-border-radius, 12px)!important;box-shadow:var(--p-dialog-shadow, 0 20px 25px -5px rgba(0, 0, 0, .1), 0 8px 10px -6px rgba(0, 0, 0, .1))!important}::ng-deep body{--vl-dock-bottom-gap: max(8px, env(safe-area-inset-bottom, 0px))}\n"] }]
9007
8542
  }], ctorParameters: () => [] });
9008
8543
 
8544
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-15
8545
+ // - new: exports MediaSourcePipe (vlMedia, #145)
9009
8546
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-14
9010
8547
  // - new: exports MobileOidcService (one OpenID Connect flow for Keycloak and Okta on the devices)
9011
8548
  // Faramarz Yousefian and Agent Toni (Claude Opus 5.5) 2026-10-13
@@ -9020,5 +8557,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
9020
8557
  * Generated bundle index. Do not edit.
9021
8558
  */
9022
8559
 
9023
- export { AppVersionResetService, CardComponent, ConfigService, Couchdb, CustomKeyboardComponent, CustomKeyboardDirective, CustomKeyboardService, FormComponent, GridComponent, HeaderMenuComponent, LogsComponent, MobileKeycloakService, MobileOidcService, MobileOktaService, ModelComponent, NetworkLogService, NotificationService, PaginationComponent, PlaceholderPipe, REQUEST_STAGES, RemoteListService, RequestService, RequestTimeoutError, RequestTimeoutService, SIDE_DOCK_CLASS, SideMenuComponent, UtilityService, ViewToolkitModule, isNativeMobile, isNativePhone, normalizeCustomKeyboardType, resolveCustomKeyboardType, resolvePlaceholder, sideDockWidth, stagedRequest };
8560
+ export { AppVersionResetService, BACKEND_PROXY_PREFIX, CardComponent, ConfigService, Couchdb, CustomKeyboardComponent, CustomKeyboardDirective, CustomKeyboardService, FormComponent, GridComponent, HeaderMenuComponent, LogsComponent, MediaSourcePipe, MobileKeycloakService, MobileOidcService, MobileOktaService, ModelComponent, NetworkLogService, NotificationService, PaginationComponent, PlaceholderPipe, REQUEST_STAGES, RemoteListService, RequestService, RequestTimeoutError, RequestTimeoutService, SIDE_DOCK_CLASS, SideMenuComponent, UtilityService, ViewToolkitModule, backendUrl, isNativeMobile, isNativePhone, normalizeCustomKeyboardType, resolveCustomKeyboardType, resolvePlaceholder, setToolkitKeyboardOnWeb, sideDockWidth, stagedRequest, toolkitKeyboardActive };
9024
8561
  //# sourceMappingURL=yomotech-view-toolkit.mjs.map