@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
|
|
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,
|
|
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
|
|
7
|
-
import * as i1$
|
|
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$
|
|
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
|
|
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$
|
|
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:
|
|
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
|
-
|
|
476
|
-
if (!
|
|
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 (!
|
|
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
|
-
!
|
|
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
|
-
|
|
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
|
-
|
|
1669
|
-
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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
|
|
2345
|
-
|
|
2422
|
+
get isNativeMobile() {
|
|
2423
|
+
return toolkitKeyboardActive();
|
|
2424
|
+
}
|
|
2346
2425
|
/** panelStyleClass of a date picker. */
|
|
2347
|
-
datePanelClass
|
|
2348
|
-
|
|
2349
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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$
|
|
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$
|
|
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
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
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:
|
|
3492
|
-
clientId:
|
|
3493
|
-
redirectUri:
|
|
3494
|
-
logoutRedirectUri:
|
|
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:
|
|
3502
|
-
clientId:
|
|
3503
|
-
|
|
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
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
6837
|
-
if (!mapping
|
|
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
|
-
|
|
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]?.
|
|
7047
|
-
this.modalItemInfo.propertySchema[scope]?.
|
|
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
|
|
7058
|
-
|
|
7059
|
-
const
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
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(
|
|
7078
|
-
|
|
7079
|
-
toDateTime
|
|
7080
|
-
|
|
7081
|
-
|
|
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
|
-
|
|
7097
|
-
|
|
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
|
-
|
|
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: [
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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 (
|
|
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$
|
|
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$
|
|
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
|