@xiriframework/xiri-ng 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/xiriframework-xiri-ng.mjs +503 -187
- package/fesm2022/xiriframework-xiri-ng.mjs.map +1 -1
- package/package.json +1 -1
- package/skills/xiri-ng-expert/SKILL.md +29 -2
- package/skills/xiri-ng-expert/references/components.md +13 -1
- package/skills/xiri-ng-expert/references/form-fields.md +75 -0
- package/skills/xiri-ng-expert/references/table.md +11 -7
- package/types/xiriframework-xiri-ng.d.ts +27 -0
- package/types/xiriframework-xiri-ng.d.ts.map +1 -1
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, Pipe, input, computed, Directive, ElementRef, ChangeDetectorRef, Input, Component, PLATFORM_ID, signal, effect, Service, output, numberAttribute, afterRenderEffect, booleanAttribute, viewChild,
|
|
3
|
+
import { inject, Pipe, input, computed, Directive, Injectable, ElementRef, ChangeDetectorRef, Input, Component, PLATFORM_ID, signal, effect, Service, output, numberAttribute, afterRenderEffect, booleanAttribute, viewChild, DestroyRef, forwardRef, untracked, Injector, afterNextRender, ViewEncapsulation, linkedSignal, ChangeDetectionStrategy, NgZone, makeEnvironmentProviders } from '@angular/core';
|
|
4
4
|
import { DefaultUrlSerializer, RouterLink, Router, NavigationEnd } from '@angular/router';
|
|
5
5
|
import { takeUntilDestroyed, toSignal, rxResource } from '@angular/core/rxjs-interop';
|
|
6
6
|
import * as i1 from '@angular/forms';
|
|
7
7
|
import { NgControl, FormGroup, FormControl, FormsModule, ReactiveFormsModule, NgForm, FormGroupDirective, Validators, FormBuilder, UntypedFormControl, NG_VALUE_ACCESSOR, UntypedFormBuilder } from '@angular/forms';
|
|
8
|
+
import { tap, Subject, Subscription, debounceTime, distinctUntilChanged, BehaviorSubject, ReplaySubject, switchMap, catchError, of, merge, filter as filter$1, map, timer, EMPTY, throwError } from 'rxjs';
|
|
9
|
+
import { HttpClient } from '@angular/common/http';
|
|
10
|
+
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
8
11
|
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
|
9
12
|
import { MatFormField, MatLabel, MatSuffix, MatHint, MatError, MatFormFieldControl, MatPrefix } from '@angular/material/form-field';
|
|
10
|
-
import { Subject, Subscription, tap, debounceTime, distinctUntilChanged, BehaviorSubject, ReplaySubject, switchMap, catchError, of, map, throwError, timer, filter as filter$1 } from 'rxjs';
|
|
11
13
|
import { FocusMonitor, CdkTrapFocus, ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
|
|
12
14
|
import { MatIcon } from '@angular/material/icon';
|
|
13
15
|
import { MatIconButton, MatButton, MatFabButton, MatMiniFabButton } from '@angular/material/button';
|
|
@@ -25,8 +27,6 @@ import { enGB } from 'date-fns/locale/en-GB';
|
|
|
25
27
|
import { MatDatepickerInput, MatDatepickerToggle, MatDatepickerToggleIcon, MatDatepicker, MatDateRangeInput, MatStartDate, MatEndDate, MatDateRangePicker } from '@angular/material/datepicker';
|
|
26
28
|
import { MatTree, MatTreeNestedDataSource, MatTreeNode, MatTreeNodeToggle, MatTreeNodePadding, MatTreeNodeDef } from '@angular/material/tree';
|
|
27
29
|
import { SelectionModel } from '@angular/cdk/collections';
|
|
28
|
-
import { HttpClient } from '@angular/common/http';
|
|
29
|
-
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
30
30
|
import { filter, takeUntil, debounceTime as debounceTime$1 } from 'rxjs/operators';
|
|
31
31
|
import * as i2 from 'ngx-mat-select-search';
|
|
32
32
|
import { NgxMatSelectSearchModule } from 'ngx-mat-select-search';
|
|
@@ -119,6 +119,148 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
119
119
|
}]
|
|
120
120
|
}], propDecorators: { xiriCols: [{ type: i0.Input, args: [{ isSignal: true, alias: "xiriCols", required: false }] }] } });
|
|
121
121
|
|
|
122
|
+
class XiriSnackbarService {
|
|
123
|
+
constructor() {
|
|
124
|
+
this.snackbar = inject(MatSnackBar);
|
|
125
|
+
}
|
|
126
|
+
success(message, duration = 3000, action) {
|
|
127
|
+
return this.show(message, 'success', duration, action);
|
|
128
|
+
}
|
|
129
|
+
error(message, duration = 5000, action) {
|
|
130
|
+
return this.show(message, 'error', duration, action);
|
|
131
|
+
}
|
|
132
|
+
info(message, duration = 3000, action) {
|
|
133
|
+
return this.show(message, 'info', duration, action);
|
|
134
|
+
}
|
|
135
|
+
warning(message, duration = 4000, action) {
|
|
136
|
+
return this.show(message, 'warning', duration, action);
|
|
137
|
+
}
|
|
138
|
+
handleResponse(response) {
|
|
139
|
+
const res = response;
|
|
140
|
+
if (!res?.message || !res?.messageType)
|
|
141
|
+
return false;
|
|
142
|
+
switch (res.messageType) {
|
|
143
|
+
case 'success':
|
|
144
|
+
this.success(res.message);
|
|
145
|
+
break;
|
|
146
|
+
case 'error':
|
|
147
|
+
this.error(res.message);
|
|
148
|
+
break;
|
|
149
|
+
case 'info':
|
|
150
|
+
this.info(res.message);
|
|
151
|
+
break;
|
|
152
|
+
case 'warning':
|
|
153
|
+
this.warning(res.message);
|
|
154
|
+
break;
|
|
155
|
+
default: this.info(res.message);
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
show(message, type, duration, action) {
|
|
160
|
+
const config = {
|
|
161
|
+
duration: duration,
|
|
162
|
+
horizontalPosition: 'center',
|
|
163
|
+
verticalPosition: 'bottom',
|
|
164
|
+
panelClass: [`xiri-snackbar-${type}`],
|
|
165
|
+
politeness: type === 'error' ? 'assertive' : 'polite',
|
|
166
|
+
};
|
|
167
|
+
return this.snackbar.open(message, action || undefined, config);
|
|
168
|
+
}
|
|
169
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriSnackbarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
170
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriSnackbarService, providedIn: 'root' }); }
|
|
171
|
+
}
|
|
172
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriSnackbarService, decorators: [{
|
|
173
|
+
type: Injectable,
|
|
174
|
+
args: [{
|
|
175
|
+
providedIn: 'root',
|
|
176
|
+
}]
|
|
177
|
+
}] });
|
|
178
|
+
|
|
179
|
+
// providedIn 'root' so the class doubles as its own default: an app that never calls
|
|
180
|
+
// provideXiriServices() gets the documented '/api/' base instead of a NullInjector error.
|
|
181
|
+
// provideXiriServices() still overrides it with a useValue provider.
|
|
182
|
+
class XiriDataServiceConfig {
|
|
183
|
+
constructor() {
|
|
184
|
+
this.api = '/api/';
|
|
185
|
+
}
|
|
186
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataServiceConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
187
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataServiceConfig, providedIn: 'root' }); }
|
|
188
|
+
}
|
|
189
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataServiceConfig, decorators: [{
|
|
190
|
+
type: Injectable,
|
|
191
|
+
args: [{ providedIn: 'root' }]
|
|
192
|
+
}] });
|
|
193
|
+
const defaultXiriDataServiceConfig = {
|
|
194
|
+
api: '/api/',
|
|
195
|
+
};
|
|
196
|
+
class XiriDataService {
|
|
197
|
+
constructor() {
|
|
198
|
+
this.http = inject(HttpClient);
|
|
199
|
+
this.config = inject(XiriDataServiceConfig);
|
|
200
|
+
this.snackbar = inject(XiriSnackbarService);
|
|
201
|
+
}
|
|
202
|
+
get(url) {
|
|
203
|
+
return this.http.get(this.config.api + url).pipe(tap((res) => this.snackbar.handleResponse(res)));
|
|
204
|
+
}
|
|
205
|
+
post(url, data) {
|
|
206
|
+
return this.http.post(this.config.api + url, data).pipe(tap((res) => this.snackbar.handleResponse(res)));
|
|
207
|
+
}
|
|
208
|
+
postFile(url, data) {
|
|
209
|
+
return this.http.post(this.config.api + url, data, { responseType: 'blob' });
|
|
210
|
+
}
|
|
211
|
+
postFileResponse(url, data) {
|
|
212
|
+
return this.http.post(this.config.api + url, data, { observe: 'response', responseType: 'blob' });
|
|
213
|
+
}
|
|
214
|
+
/** @deprecated Use postFileResponse() + XiriDownloadService.download() instead */
|
|
215
|
+
postDownload(url, data) {
|
|
216
|
+
console.warn('XiriDataService.postDownload() is deprecated. Use postFileResponse() + XiriDownloadService.download() instead.');
|
|
217
|
+
const downForm = document.createElement('form');
|
|
218
|
+
downForm.target = '_blank';
|
|
219
|
+
downForm.method = 'POST';
|
|
220
|
+
downForm.action = url;
|
|
221
|
+
const input = document.createElement('input');
|
|
222
|
+
input.type = 'hidden';
|
|
223
|
+
input.name = 'data';
|
|
224
|
+
input.setAttribute('value', JSON.stringify(data));
|
|
225
|
+
downForm.appendChild(input);
|
|
226
|
+
document.body.appendChild(downForm);
|
|
227
|
+
downForm.submit();
|
|
228
|
+
}
|
|
229
|
+
getConfigApi() {
|
|
230
|
+
return this.config.api;
|
|
231
|
+
}
|
|
232
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
233
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataService, providedIn: 'root' }); }
|
|
234
|
+
}
|
|
235
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataService, decorators: [{
|
|
236
|
+
type: Injectable,
|
|
237
|
+
args: [{
|
|
238
|
+
providedIn: 'root',
|
|
239
|
+
}]
|
|
240
|
+
}] });
|
|
241
|
+
|
|
242
|
+
function parseHttpError(err) {
|
|
243
|
+
if (err.status === 400 || err.status === 424)
|
|
244
|
+
return err.error?.error || 'Format Error';
|
|
245
|
+
if (err.status === 403)
|
|
246
|
+
return err.error?.error || 'Access denied';
|
|
247
|
+
if (err.status === 404)
|
|
248
|
+
return 'Not found';
|
|
249
|
+
if (err.status === 0)
|
|
250
|
+
return 'Connection error';
|
|
251
|
+
return 'Unknown error';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// The "empty" value used to clear a control, matching the shape the control currently holds.
|
|
255
|
+
function emptyValueForField(field) {
|
|
256
|
+
const value = field.control?.value;
|
|
257
|
+
if (Array.isArray(value))
|
|
258
|
+
return [];
|
|
259
|
+
if (typeof value === 'string')
|
|
260
|
+
return '';
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
122
264
|
class XiriFileComponent {
|
|
123
265
|
static { this.nextId = 0; }
|
|
124
266
|
constructor() {
|
|
@@ -1593,117 +1735,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
1593
1735
|
type: Input
|
|
1594
1736
|
}] } });
|
|
1595
1737
|
|
|
1596
|
-
class XiriSnackbarService {
|
|
1597
|
-
constructor() {
|
|
1598
|
-
this.snackbar = inject(MatSnackBar);
|
|
1599
|
-
}
|
|
1600
|
-
success(message, duration = 3000, action) {
|
|
1601
|
-
return this.show(message, 'success', duration, action);
|
|
1602
|
-
}
|
|
1603
|
-
error(message, duration = 5000, action) {
|
|
1604
|
-
return this.show(message, 'error', duration, action);
|
|
1605
|
-
}
|
|
1606
|
-
info(message, duration = 3000, action) {
|
|
1607
|
-
return this.show(message, 'info', duration, action);
|
|
1608
|
-
}
|
|
1609
|
-
warning(message, duration = 4000, action) {
|
|
1610
|
-
return this.show(message, 'warning', duration, action);
|
|
1611
|
-
}
|
|
1612
|
-
handleResponse(response) {
|
|
1613
|
-
const res = response;
|
|
1614
|
-
if (!res?.message || !res?.messageType)
|
|
1615
|
-
return false;
|
|
1616
|
-
switch (res.messageType) {
|
|
1617
|
-
case 'success':
|
|
1618
|
-
this.success(res.message);
|
|
1619
|
-
break;
|
|
1620
|
-
case 'error':
|
|
1621
|
-
this.error(res.message);
|
|
1622
|
-
break;
|
|
1623
|
-
case 'info':
|
|
1624
|
-
this.info(res.message);
|
|
1625
|
-
break;
|
|
1626
|
-
case 'warning':
|
|
1627
|
-
this.warning(res.message);
|
|
1628
|
-
break;
|
|
1629
|
-
default: this.info(res.message);
|
|
1630
|
-
}
|
|
1631
|
-
return true;
|
|
1632
|
-
}
|
|
1633
|
-
show(message, type, duration, action) {
|
|
1634
|
-
const config = {
|
|
1635
|
-
duration: duration,
|
|
1636
|
-
horizontalPosition: 'center',
|
|
1637
|
-
verticalPosition: 'bottom',
|
|
1638
|
-
panelClass: [`xiri-snackbar-${type}`],
|
|
1639
|
-
politeness: type === 'error' ? 'assertive' : 'polite',
|
|
1640
|
-
};
|
|
1641
|
-
return this.snackbar.open(message, action || undefined, config);
|
|
1642
|
-
}
|
|
1643
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriSnackbarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1644
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriSnackbarService, providedIn: 'root' }); }
|
|
1645
|
-
}
|
|
1646
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriSnackbarService, decorators: [{
|
|
1647
|
-
type: Injectable,
|
|
1648
|
-
args: [{
|
|
1649
|
-
providedIn: 'root',
|
|
1650
|
-
}]
|
|
1651
|
-
}] });
|
|
1652
|
-
|
|
1653
|
-
class XiriDataServiceConfig {
|
|
1654
|
-
constructor() {
|
|
1655
|
-
this.api = '/api/';
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
const defaultXiriDataServiceConfig = {
|
|
1659
|
-
api: '/api/',
|
|
1660
|
-
};
|
|
1661
|
-
class XiriDataService {
|
|
1662
|
-
constructor() {
|
|
1663
|
-
this.http = inject(HttpClient);
|
|
1664
|
-
this.config = inject(XiriDataServiceConfig);
|
|
1665
|
-
this.snackbar = inject(XiriSnackbarService);
|
|
1666
|
-
}
|
|
1667
|
-
get(url) {
|
|
1668
|
-
return this.http.get(this.config.api + url).pipe(tap((res) => this.snackbar.handleResponse(res)));
|
|
1669
|
-
}
|
|
1670
|
-
post(url, data) {
|
|
1671
|
-
return this.http.post(this.config.api + url, data).pipe(tap((res) => this.snackbar.handleResponse(res)));
|
|
1672
|
-
}
|
|
1673
|
-
postFile(url, data) {
|
|
1674
|
-
return this.http.post(this.config.api + url, data, { responseType: 'blob' });
|
|
1675
|
-
}
|
|
1676
|
-
postFileResponse(url, data) {
|
|
1677
|
-
return this.http.post(this.config.api + url, data, { observe: 'response', responseType: 'blob' });
|
|
1678
|
-
}
|
|
1679
|
-
/** @deprecated Use postFileResponse() + XiriDownloadService.download() instead */
|
|
1680
|
-
postDownload(url, data) {
|
|
1681
|
-
console.warn('XiriDataService.postDownload() is deprecated. Use postFileResponse() + XiriDownloadService.download() instead.');
|
|
1682
|
-
const downForm = document.createElement('form');
|
|
1683
|
-
downForm.target = '_blank';
|
|
1684
|
-
downForm.method = 'POST';
|
|
1685
|
-
downForm.action = url;
|
|
1686
|
-
const input = document.createElement('input');
|
|
1687
|
-
input.type = 'hidden';
|
|
1688
|
-
input.name = 'data';
|
|
1689
|
-
input.setAttribute('value', JSON.stringify(data));
|
|
1690
|
-
downForm.appendChild(input);
|
|
1691
|
-
document.body.appendChild(downForm);
|
|
1692
|
-
downForm.submit();
|
|
1693
|
-
}
|
|
1694
|
-
getConfigApi() {
|
|
1695
|
-
return this.config.api;
|
|
1696
|
-
}
|
|
1697
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1698
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataService, providedIn: 'root' }); }
|
|
1699
|
-
}
|
|
1700
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriDataService, decorators: [{
|
|
1701
|
-
type: Injectable,
|
|
1702
|
-
args: [{
|
|
1703
|
-
providedIn: 'root',
|
|
1704
|
-
}]
|
|
1705
|
-
}] });
|
|
1706
|
-
|
|
1707
1738
|
class XiriSearchComponent {
|
|
1708
1739
|
constructor() {
|
|
1709
1740
|
this.changed = output();
|
|
@@ -1822,13 +1853,10 @@ class XiriTreeselectComponent extends XiriFieldMain {
|
|
|
1822
1853
|
return;
|
|
1823
1854
|
this.dataSource.data = data;
|
|
1824
1855
|
// this.tree().expandAll();
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
this.treeItemSelectionToggle(node);
|
|
1830
|
-
});
|
|
1831
|
-
});
|
|
1856
|
+
// Nicht direkt togglen: ein Options-Reload ersetzt das Feld-Objekt und baut den Baum
|
|
1857
|
+
// neu auf, wobei die alten Node-Objekte im SelectionModel selektiert bleiben würden
|
|
1858
|
+
// (doppelte IDs) und das Zurückschreiben ans FormControl emittieren würde.
|
|
1859
|
+
this.applyInputSelection();
|
|
1832
1860
|
this.loading.set(false);
|
|
1833
1861
|
}));
|
|
1834
1862
|
}
|
|
@@ -2290,6 +2318,8 @@ class XiriFormFieldsComponent {
|
|
|
2290
2318
|
this.destroyRef = inject(DestroyRef);
|
|
2291
2319
|
this.elementRef = inject(ElementRef);
|
|
2292
2320
|
this.localeService = inject(XiriLocaleService);
|
|
2321
|
+
this.dataService = inject(XiriDataService);
|
|
2322
|
+
this.snackbar = inject(XiriSnackbarService);
|
|
2293
2323
|
this.form = input(null, /* @ts-ignore */
|
|
2294
2324
|
...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
2295
2325
|
this.display = input('full', /* @ts-ignore */
|
|
@@ -2306,6 +2336,37 @@ class XiriFormFieldsComponent {
|
|
|
2306
2336
|
this._fieldsLoaded = false;
|
|
2307
2337
|
this._initialEmitDone = false;
|
|
2308
2338
|
this._autoFocusDone = false;
|
|
2339
|
+
// Render-Kopien der gepatchten Felder, siehe displayFields(). Das Signal daneben stößt nur die
|
|
2340
|
+
// Neuberechnung an; die Identitäten selbst leben in der Map, damit sie stabil bleiben.
|
|
2341
|
+
this.patchedClones = new Map();
|
|
2342
|
+
this.patchVersion = signal(0, /* @ts-ignore */
|
|
2343
|
+
...(ngDevMode ? [{ debugName: "patchVersion" }] : /* istanbul ignore next */ []));
|
|
2344
|
+
// Zuletzt angewendeter Patch pro Feld - Rohdaten vom Server, also frei von den Referenzen,
|
|
2345
|
+
// die z.B. der Treeselect nachträglich in die Optionsliste hängt.
|
|
2346
|
+
this.lastPatch = new Map();
|
|
2347
|
+
// Zählt jeden Aufbau einer neuen Feldliste, damit der Initial-Reload auch dann feuert, wenn
|
|
2348
|
+
// das neue Formular zufällig dieselben Trigger-Werte trägt wie das alte.
|
|
2349
|
+
this.formGeneration = 0;
|
|
2350
|
+
// Startet den Reload einmalig nach dem Aufbau der Controls.
|
|
2351
|
+
this.reloadKick = new Subject();
|
|
2352
|
+
// Unterdrückt den formChange-Emit der valueChanges-Subscription, während ein Patch angewendet
|
|
2353
|
+
// wird - nach dem Patch wird genau einmal emittiert.
|
|
2354
|
+
this.applyingPatch = false;
|
|
2355
|
+
// Gepatchte Felder werden als Kopie gerendert, damit Kind-Komponenten mit eigenem State
|
|
2356
|
+
// (treeselect, chips, die Select-Directive) ihre Inputs neu auswerten. Die Originale bleiben
|
|
2357
|
+
// die Quelle der Wahrheit - sie tragen field.control und werden von xiri-query mitgelesen.
|
|
2358
|
+
//
|
|
2359
|
+
// Die Kopie wird gecacht: die Identität eines Felds darf sich nur ändern, wenn genau dieses
|
|
2360
|
+
// Feld erneut gepatcht wurde. Würde hier bei jedem Patch neu geklont, liefe der Input-Setter
|
|
2361
|
+
// aller je gepatchten Felder erneut - beim Treeselect wäre das ein kompletter Neuaufbau.
|
|
2362
|
+
this.displayFields = computed(() => {
|
|
2363
|
+
const fields = this.fields();
|
|
2364
|
+
this.patchVersion();
|
|
2365
|
+
if (fields === null || this.patchedClones.size === 0)
|
|
2366
|
+
return fields;
|
|
2367
|
+
return fields.map(f => this.patchedClones.get(f.id) ?? f);
|
|
2368
|
+
}, /* @ts-ignore */
|
|
2369
|
+
...(ngDevMode ? [{ debugName: "displayFields" }] : /* istanbul ignore next */ []));
|
|
2309
2370
|
this.fields = computed(() => {
|
|
2310
2371
|
this._fieldsLoaded = false;
|
|
2311
2372
|
if (this._fields !== null) {
|
|
@@ -2319,6 +2380,10 @@ class XiriFormFieldsComponent {
|
|
|
2319
2380
|
return null;
|
|
2320
2381
|
}
|
|
2321
2382
|
this._fields = fields;
|
|
2383
|
+
// Neue Feldliste: alte Render-Kopien und Patch-Stände gehören zum vorherigen Formular.
|
|
2384
|
+
this.formGeneration++;
|
|
2385
|
+
this.patchedClones.clear();
|
|
2386
|
+
this.lastPatch.clear();
|
|
2322
2387
|
const initialCollapsed = new Set();
|
|
2323
2388
|
for (const f of fields) {
|
|
2324
2389
|
if (f.type === 'header' && f.collapsible && f.collapsed)
|
|
@@ -2381,13 +2446,23 @@ class XiriFormFieldsComponent {
|
|
|
2381
2446
|
}
|
|
2382
2447
|
}
|
|
2383
2448
|
});
|
|
2449
|
+
// Ein Reload direkt nach dem Aufbau der Controls, immer. Der Server liefert seine Listen
|
|
2450
|
+
// zum Startwert eines Triggers - der muss aber nicht der aktuelle sein: xiri-query stellt
|
|
2451
|
+
// gespeicherte Filterwerte über formService.loadState() wieder her, bevor die Controls
|
|
2452
|
+
// entstehen. Ein bedingtes "nur wenn abweichend" ist von hier aus nicht erkennbar.
|
|
2453
|
+
effect(() => {
|
|
2454
|
+
const formInput = this.form();
|
|
2455
|
+
this.fields();
|
|
2456
|
+
if (formInput !== null && this._fieldsLoaded)
|
|
2457
|
+
this.reloadKick.next();
|
|
2458
|
+
});
|
|
2384
2459
|
}
|
|
2385
2460
|
ngOnInit() {
|
|
2386
2461
|
this.check()?.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
|
2387
2462
|
this.validateAllFormFields();
|
|
2388
2463
|
});
|
|
2389
2464
|
this.formGroup.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
|
2390
|
-
if (this._fieldsLoaded) {
|
|
2465
|
+
if (this._fieldsLoaded && !this.applyingPatch) {
|
|
2391
2466
|
const currentValue = JSON.stringify(this.formGroup.value);
|
|
2392
2467
|
if (currentValue === this.lastValue)
|
|
2393
2468
|
return;
|
|
@@ -2395,6 +2470,122 @@ class XiriFormFieldsComponent {
|
|
|
2395
2470
|
this.formChange.emit(this.formGroup);
|
|
2396
2471
|
}
|
|
2397
2472
|
});
|
|
2473
|
+
// Ein Trigger-Wechsel lädt die abhängigen Felder nach.
|
|
2474
|
+
//
|
|
2475
|
+
// Der Snapshot wird ungefiltert gebildet und erst danach entprellt: das äußere switchMap
|
|
2476
|
+
// bricht damit einen noch laufenden Request sofort ab, sobald sich der Stand ändert. Läge
|
|
2477
|
+
// debounceTime davor, bliebe die Anfrage zum überholten Stand noch 200 ms aktiv und könnte
|
|
2478
|
+
// in diesem Fenster antworten - ihr Patch würde dann Werte verwerfen, die für den neuen
|
|
2479
|
+
// Stand gültig sind, und kein späterer Patch stellt sie wieder her.
|
|
2480
|
+
merge(this.reloadKick, this.formGroup.valueChanges).pipe(
|
|
2481
|
+
// Während des Control-Aufbaus feuert valueChanges für jedes entfernte und neue Control;
|
|
2482
|
+
// diese Zwischenstände sind kein Trigger-Wechsel. Der Kick danach übernimmt.
|
|
2483
|
+
filter$1(() => this._fieldsLoaded), map(() => JSON.stringify({ g: this.formGeneration, v: this.triggerValues() })), distinctUntilChanged(), switchMap(() => timer(200).pipe(switchMap(() => this.fetchPatches()))), takeUntilDestroyed(this.destroyRef)).subscribe(patch => this.applyPatch(patch.url, patch.fields));
|
|
2484
|
+
}
|
|
2485
|
+
// Felder, die inhaltlich von anderen abhängen (beide Angaben nötig, siehe field.interface).
|
|
2486
|
+
dependentFields() {
|
|
2487
|
+
return (this._fields ?? []).filter(f => !!f.reloadUrl && !!f.reloadOn?.length);
|
|
2488
|
+
}
|
|
2489
|
+
// Nur die Trigger-Werte gehen an den Server, nicht das ganze Formular: ein Options-Endpoint
|
|
2490
|
+
// braucht keine Passwörter. Alles Weitere gehört in die reloadUrl.
|
|
2491
|
+
//
|
|
2492
|
+
// Ohne URL sind es alle Trigger (für den Änderungsvergleich), mit URL nur die, von denen die
|
|
2493
|
+
// Felder genau dieser URL abhängen - jeder Endpoint sieht also nur seine eigenen Werte.
|
|
2494
|
+
triggerValues(url) {
|
|
2495
|
+
const dependent = this.dependentFields().filter(f => url === undefined || f.reloadUrl === url);
|
|
2496
|
+
if (dependent.length === 0)
|
|
2497
|
+
return null;
|
|
2498
|
+
const raw = this.formGroup.getRawValue();
|
|
2499
|
+
const values = {};
|
|
2500
|
+
for (const field of dependent)
|
|
2501
|
+
for (const trigger of field.reloadOn ?? [])
|
|
2502
|
+
if (trigger in raw)
|
|
2503
|
+
values[trigger] = raw[trigger];
|
|
2504
|
+
return values;
|
|
2505
|
+
}
|
|
2506
|
+
fetchPatches() {
|
|
2507
|
+
const urls = new Set(this.dependentFields().map(f => f.reloadUrl));
|
|
2508
|
+
if (urls.size === 0)
|
|
2509
|
+
return EMPTY;
|
|
2510
|
+
// Ein Request pro URL, jeder für sich: schlägt einer fehl, werden die Patches der
|
|
2511
|
+
// anderen trotzdem angewendet.
|
|
2512
|
+
return merge(...Array.from(urls).map(url => this.dataService.post(url, this.triggerValues(url) ?? {}).pipe(map(res => ({ url, fields: res?.fields ?? {} })), catchError(err => {
|
|
2513
|
+
this.snackbar.error(parseHttpError(err));
|
|
2514
|
+
return EMPTY;
|
|
2515
|
+
}))));
|
|
2516
|
+
}
|
|
2517
|
+
applyPatch(url, patch) {
|
|
2518
|
+
const fields = this._fields;
|
|
2519
|
+
if (fields === null || !patch)
|
|
2520
|
+
return;
|
|
2521
|
+
const patchedNow = new Set();
|
|
2522
|
+
this.applyingPatch = true;
|
|
2523
|
+
try {
|
|
2524
|
+
for (const field of fields) {
|
|
2525
|
+
// Der Server ist eine Trust-Boundary: eine Antwort darf nur die abhängigen Felder
|
|
2526
|
+
// genau dieser URL anfassen.
|
|
2527
|
+
if (!field.reloadOn?.length || field.reloadUrl !== url)
|
|
2528
|
+
continue;
|
|
2529
|
+
const entry = patch[field.id];
|
|
2530
|
+
if (entry === null || typeof entry !== 'object')
|
|
2531
|
+
continue;
|
|
2532
|
+
const applied = applyPatchProperties(field, entry, this.lastPatch.get(field.id));
|
|
2533
|
+
this.lastPatch.set(field.id, entry);
|
|
2534
|
+
if (applied.length === 0)
|
|
2535
|
+
continue;
|
|
2536
|
+
patchedNow.add(field.id);
|
|
2537
|
+
this.patchedClones.set(field.id, { ...field });
|
|
2538
|
+
const control = field.control;
|
|
2539
|
+
if (!control)
|
|
2540
|
+
continue;
|
|
2541
|
+
if (applied.includes('list'))
|
|
2542
|
+
this.pruneValue(field, control);
|
|
2543
|
+
if (applied.includes('required') || applied.includes('min') || applied.includes('max')) {
|
|
2544
|
+
field.validations = undefined;
|
|
2545
|
+
control.setValidators(this.bindValidations(field));
|
|
2546
|
+
control.updateValueAndValidity({ emitEvent: false });
|
|
2547
|
+
}
|
|
2548
|
+
// Mit dem globalen disabled kombinieren, sonst reaktiviert ein disabled:false im
|
|
2549
|
+
// Patch ein Control, obwohl die ganze Form gerade deaktiviert ist.
|
|
2550
|
+
if (applied.includes('disabled')) {
|
|
2551
|
+
if (field.disabled || this.disabled())
|
|
2552
|
+
control.disable({ emitEvent: false });
|
|
2553
|
+
else
|
|
2554
|
+
control.enable({ emitEvent: false });
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
finally {
|
|
2559
|
+
this.applyingPatch = false;
|
|
2560
|
+
}
|
|
2561
|
+
if (patchedNow.size === 0)
|
|
2562
|
+
return;
|
|
2563
|
+
// Nur anstoßen: die Klon-Identitäten selbst liegen in patchedClones und bleiben für alle
|
|
2564
|
+
// Felder stabil, die dieser Patch nicht angefasst hat.
|
|
2565
|
+
this.patchVersion.update(v => v + 1);
|
|
2566
|
+
this.lastValue = JSON.stringify(this.formGroup.value);
|
|
2567
|
+
this.formChange.emit(this.formGroup);
|
|
2568
|
+
}
|
|
2569
|
+
// Verwirft Werte, die die neue Liste nicht mehr anbietet. Das emittiert bewusst, damit ein
|
|
2570
|
+
// Feld, das seinerseits von diesem abhängt, nachzieht. Die Kette terminiert, weil Pruning
|
|
2571
|
+
// monoton ist: ein leeres Feld hat nichts mehr zu verwerfen.
|
|
2572
|
+
pruneValue(field, control) {
|
|
2573
|
+
// Nur wenn die Liste autoritativ ist. Bei Server-Suche ist list nur der statische Sockel,
|
|
2574
|
+
// bei chips sind es Vorschläge - dort würde Pruning gültige Eingaben löschen.
|
|
2575
|
+
if (field.url || field.type === 'chips')
|
|
2576
|
+
return;
|
|
2577
|
+
const ids = collectOptionIds(field.list ?? []);
|
|
2578
|
+
const value = control.value;
|
|
2579
|
+
if (Array.isArray(value)) {
|
|
2580
|
+
const kept = value.filter(v => ids.has(v));
|
|
2581
|
+
if (kept.length !== value.length)
|
|
2582
|
+
control.setValue(kept);
|
|
2583
|
+
return;
|
|
2584
|
+
}
|
|
2585
|
+
if (value === null || value === undefined || value === '')
|
|
2586
|
+
return;
|
|
2587
|
+
if (!ids.has(value))
|
|
2588
|
+
control.setValue(emptyValueForField(field));
|
|
2398
2589
|
}
|
|
2399
2590
|
createControl() {
|
|
2400
2591
|
const fields = this._fields;
|
|
@@ -2663,7 +2854,9 @@ class XiriFormFieldsComponent {
|
|
|
2663
2854
|
// Ein header startet seine eigene Section und ist nie Teil einer vorherigen
|
|
2664
2855
|
if (field.type === 'header')
|
|
2665
2856
|
return false;
|
|
2666
|
-
|
|
2857
|
+
// Über die ID suchen, nicht über die Identität: gepatchte Felder werden als Kopie
|
|
2858
|
+
// gerendert, damit die Kind-Komponenten eine neue Input-Identität sehen.
|
|
2859
|
+
const idx = fields.findIndex(f => f.id === field.id);
|
|
2667
2860
|
if (idx <= 0)
|
|
2668
2861
|
return false;
|
|
2669
2862
|
// Walk backwards to find the nearest header or divider
|
|
@@ -2703,7 +2896,7 @@ class XiriFormFieldsComponent {
|
|
|
2703
2896
|
}
|
|
2704
2897
|
}
|
|
2705
2898
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriFormFieldsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2706
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: XiriFormFieldsComponent, isStandalone: true, selector: "xiri-form-fields", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, check: { classPropertyName: "check", publicName: "check", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formChange: "formChange" }, ngImport: i0, template: "<form class=\"xrow dynamic-form\" [formGroup]=\"formGroup\" [class]=\"display()\">\n\t@for (field of fields(); track field.id) {\n\t\t@if (isFieldVisible(field)) {\n\t\t@switch (field.type) {\n\t\t\t@case ('text') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [type]=\"field.subtype || 'text'\"\n\t\t\t\t\t [autocomplete]=\"field.name\"\n\t\t\t\t\t [attr.minlength]=\"field.min\"\n\t\t\t\t\t [attr.maxlength]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t@if (field.textPrefix) {\n\t\t\t\t\t\t<span matTextPrefix=\"\">{{ field.textPrefix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.textSuffix) {\n\t\t\t\t\t\t<span matTextSuffix=\"\">{{ field.textSuffix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconPrefix) {\n\t\t\t\t\t\t<mat-icon matPrefix>{{ field.iconPrefix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconSuffix) {\n\t\t\t\t\t\t<mat-icon matSuffix>{{ field.iconSuffix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('number') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [autocomplete]=\"field.name || field.id\"\n\t\t\t\t\t [lang]=\"field.locale || ''\"\n\t\t\t\t\t [type]=\"field.type\"\n\t\t\t\t\t [attr.min]=\"field.min\"\n\t\t\t\t\t [attr.max]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t@if (field.textPrefix) {\n\t\t\t\t\t\t<span matTextPrefix=\"\">{{ field.textPrefix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.textSuffix) {\n\t\t\t\t\t\t<span matTextSuffix=\"\">{{ field.textSuffix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconPrefix) {\n\t\t\t\t\t\t<mat-icon matPrefix>{{ field.iconPrefix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconSuffix) {\n\t\t\t\t\t\t<mat-icon matSuffix>{{ field.iconSuffix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('password') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [type]=\"field.pwdhide ? 'password' : 'text'\"\n\t\t\t\t\t [autocomplete]=\"field.name\"\n\t\t\t\t\t [attr.minlength]=\"field.min\"\n\t\t\t\t\t [attr.maxlength]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t<button mat-icon-button matSuffix class=\"form-field-button\" (click)=\"field.pwdhide = !field.pwdhide\" [attr.aria-label]=\"'Hide password'\"\n\t\t\t\t\t [attr.aria-pressed]=\"field.pwdhide\">\n\t\t\t\t\t\t<mat-icon>{{ field.pwdhide ? 'visibility_off' : 'visibility' }}</mat-icon>\n\t\t\t\t\t</button>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('select') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t@if (field.search === false) {\n\t\t\t\t\t\t<mat-select [formControlName]=\"field.id\" [multiple]=\"field.multiple\" [required]=\"!!field.required\" panelClass=\"selectPanel\">\n\t\t\t\t\t\t\t@for (x of field.list; track x.id) {\n\t\t\t\t\t\t\t\t<mat-option [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</mat-select>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<mat-select [formControlName]=\"field.id\" [multiple]=\"field.multiple\" [required]=\"!!field.required\" panelClass=\"selectPanel\">\n\t\t\t\t\t\t\t<mat-option>\n\t\t\t\t\t\t\t\t<ngx-mat-select-search [formControl]=\"xiriSelect.formControl\" xiriSelect #xiriSelect=\"xiriSelect\"\n\t\t\t\t\t\t\t\t [values]=\"field.list || []\" [searching]=\"xiriSelect.searching\"\n\t\t\t\t\t\t\t\t [serverSideSearch]=\"!!field.serverSideSearch\" [serverUrl]=\"field.url || ''\"\n\t\t\t\t\t\t\t\t [serverParams]=\"field.params || {}\"></ngx-mat-select-search>\n\t\t\t\t\t\t\t</mat-option>\n\t\t\t\t\t\t\t@for (x of xiriSelect.filter | async; track x.id) {\n\t\t\t\t\t\t\t\t<mat-option [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</mat-select>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('treeselect') {\n\t\t\t\t<div class=\"treeselect\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-treeselect [field]=\"field\" [formControlName]=\"field.id\"\n\t\t\t\t\t [class.error]=\"formGroup.get(field.id)?.touched && formGroup.get(field.id)?.invalid\"></xiri-treeselect>\n\t\t\t\t\t<div class=\"xhint\" aria-live=\"polite\" [class.error]=\"formGroup.get(field.id)?.touched && formGroup.get(field.id)?.invalid\">\n\t\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.touched && formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('date') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"date\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date [field]=\"field\" [formControlName]=\"field.id\"></xiri-date>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('yearmonth') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"yearmonth\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-yearmonth [field]=\"field\" [formControlName]=\"field.id\"></xiri-yearmonth>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('daterange') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"daterange\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date-range [field]=\"field\" [formControlName]=\"field.id\"></xiri-date-range>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('datetimerange') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"datetimerange\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date-time-range [field]=\"field\" [formControlName]=\"field.id\"></xiri-date-time-range>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('bool') {\n\t\t\t\t<div class=\"bool\" [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-checkbox [formControlName]=\"field.id\">{{ field.name }}</mat-checkbox>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('radio') {\n\t\t\t\t<div class=\"radio\" [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<span class=\"radio-label\" [id]=\"field.id + '-label'\">{{ field.name }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t<mat-radio-group\n\t\t\t\t\t\t[formControlName]=\"field.id\"\n\t\t\t\t\t\t[attr.aria-labelledby]=\"field.name ? field.id + '-label' : null\"\n\t\t\t\t\t>\n\t\t\t\t\t\t@for (x of field.list; track x.id) {\n\t\t\t\t\t\t\t<mat-radio-button [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-radio-button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</mat-radio-group>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('volume') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-volume [field]=\"field\" [formControlName]=\"field.id\"></xiri-volume>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('timelimit') {\n\t\t\t\t<div [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-timelimit matInput [formControlName]=\"field.id\" [required]=\"!!field.required\" [texts]=\"field.texts || {}\"></xiri-timelimit>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('textarea') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<textarea matInput [formControlName]=\"field.id\" [required]=\"!!field.required\" [rows]=\"field.rows || 5\"></textarea>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('file') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-file [field]=\"field\" [formControlName]=\"field.id\"></xiri-file>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('info') {\n\t\t\t\t<div class=\"info\" [class]=\"field.class\">{{ field.value }}</div>\n\t\t\t}\n\t\t\t@case ('header') {\n\t\t\t\t@if (field.collapsible) {\n\t\t\t\t\t<div class=\"header collapsible\" [class]=\"field.class\" role=\"button\" tabindex=\"0\"\n\t\t\t\t\t [attr.aria-expanded]=\"!isSectionCollapsed(field.id)\"\n\t\t\t\t\t (click)=\"toggleSection(field)\" (keydown.enter)=\"toggleSection(field)\" (keydown.space)=\"toggleSection(field); $event.preventDefault()\">\n\t\t\t\t\t\t<mat-icon class=\"collapse-icon\" [class.collapsed]=\"isSectionCollapsed(field.id)\">expand_more</mat-icon>\n\t\t\t\t\t\t{{ field.value }}\n\t\t\t\t\t</div>\n\t\t\t\t} @else {\n\t\t\t\t\t<div class=\"header\" [class]=\"field.class\">{{ field.value }}</div>\n\t\t\t\t}\n\t\t\t}\n\t\t\t@case ('divider') {\n\t\t\t\t<div class=\"divider\" [class]=\"field.class\">\n\t\t\t\t\t@if (field.value) {\n\t\t\t\t\t\t<span class=\"divider-text\">{{ field.value }}</span>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('html') {\n\t\t\t\t<div class=\"html\" [class]=\"field.class\" [innerHtml]=\"$any(field.value) | safeHtml\"></div>\n\t\t\t}\n\t\t\t@case ('question') {\n\t\t\t\t<div class=\"xcol question\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<div class=\"left\">\n\t\t\t\t\t\t<mat-icon [class]=\"field.iconColor || 'warn'\" inline=\"true\">{{ field.icon }}</mat-icon>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"right\">{{ field.value }}</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('waiting') {\n\t\t\t\t<div class=\"xcol waiting\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<div class=\"left\">\n\t\t\t\t\t\t@if (!field.done) {\n\t\t\t\t\t\t\t<mat-spinner diameter=\"40\"></mat-spinner>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (field.done) {\n\t\t\t\t\t\t\t<mat-icon inline=\"true\">done</mat-icon>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"right\">{{ field.value }}</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('chips') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-chips [field]=\"field\" [formControlName]=\"field.id\"></xiri-chips>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@default {\n\t\t\t\t<div>default: {{ field.type }}</div>\n\t\t\t}\n\t\t}\n\t\t}\n\t}\n</form>\n", styles: [".dynamic-form{grid-row-gap:7px}.dynamic-form mat-form-field mat-icon{--mat-icon-color: var(--xiri-formfield-icon-color)}.dynamic-form.line{grid-row-gap:5px}.dynamic-form.small{grid-row-gap:2px}.dynamic-form textarea{white-space:pre}.dynamic-form .xhint{display:flex;min-height:20px;margin-top:4px}.dynamic-form .xhint mat-hint{font-size:var(--xiri-font-size-helper);padding:1px 16px 0}.dynamic-form .xhint mat-hint.mat-mdc-form-field-hint-end{flex:1;text-align:right}.dynamic-form .xhint mat-error{font-size:var(--xiri-font-size-helper);padding:1px 16px 0}.dynamic-form .xhint.error mat-hint{display:none}.dynamic-form .date{align-self:start;min-width:min(9.5rem,100%)}.dynamic-form .date.datetime{min-width:min(14.5rem,100%)}.dynamic-form .daterange{align-self:start;min-width:min(14.5rem,100%)}.dynamic-form .datetimerange{align-self:start;min-width:min(18.5rem,100%)}.dynamic-form .bool{display:flex;flex-direction:column}.dynamic-form .bool mat-hint{font-size:var(--xiri-font-size-helper);margin-top:-8px;padding-left:44px}.dynamic-form .radio{display:flex;flex-direction:column;gap:var(--xiri-spacing-xs)}.dynamic-form .radio .radio-label{font-size:var(--xiri-font-size-label)}.dynamic-form .radio mat-radio-group{display:flex;flex-wrap:wrap;gap:var(--xiri-spacing-xs) var(--xiri-spacing-md)}.dynamic-form .radio mat-hint{font-size:var(--xiri-font-size-helper)}.dynamic-form .info{padding-top:.8rem;padding-bottom:.4rem;color:var(--primary, #2892D9)}.dynamic-form .header{font-size:var(--xiri-font-size-title);padding-top:.8rem;padding-bottom:.4rem;color:var(--primary, #2892D9)}.dynamic-form .header.collapsible{cursor:pointer;display:flex;align-items:center;gap:.25rem;-webkit-user-select:none;user-select:none;border-radius:var(--xiri-radius-xs);padding:.5rem .25rem;margin:.25rem 0}.dynamic-form .header.collapsible:hover{background:color-mix(in srgb,var(--primary, #2892D9) 8%,transparent)}.dynamic-form .header.collapsible .collapse-icon{transition:transform var(--xiri-duration-fast) var(--xiri-easing-standard)}.dynamic-form .header.collapsible .collapse-icon.collapsed{transform:rotate(-90deg)}.dynamic-form .divider{width:100%;display:flex;align-items:center;padding:.5rem 0;margin:.25rem 0}.dynamic-form .divider:before,.dynamic-form .divider:after{content:\"\";flex:1;height:1px;background:var(--outline-variant, var(--mat-sys-outline-variant, rgba(0, 0, 0, .12)))}.dynamic-form .divider .divider-text{padding:0 1rem;font-size:var(--xiri-font-size-helper);color:var(--on-surface-variant, var(--mat-sys-on-surface-variant, rgba(0, 0, 0, .6)));white-space:nowrap}.dynamic-form .question,.dynamic-form .waiting{display:flex}.dynamic-form .question .left,.dynamic-form .waiting .left{font-size:var(--xiri-font-size-display);padding-right:1rem}.dynamic-form .question{align-items:center}.dynamic-form .question .left mat-icon{height:auto;width:auto;line-height:1;overflow:visible}.dynamic-form .form-field-button{margin-left:12px;margin-right:12px}.mat-mdc-select-panel.selectPanel{max-height:max(50vh,256px)}[hidden]{display:none!important}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: NgxMatSelectSearchModule }, { kind: "component", type: i2.MatSelectSearchComponent, selector: "ngx-mat-select-search", inputs: ["placeholderLabel", "type", "closeIcon", "closeSvgIcon", "noEntriesFoundLabel", "clearSearchInput", "searching", "disableInitialFocus", "enableClearOnEscapePressed", "preventHomeEndKeyPropagation", "disableScrollToActiveOnOptionsChanged", "ariaLabel", "showToggleAllCheckbox", "toggleAllCheckboxChecked", "toggleAllCheckboxIndeterminate", "toggleAllCheckboxTooltipMessage", "toggleAllCheckboxTooltipPosition", "hideClearSearchButton", "alwaysRestoreSelectedOptionsMulti", "recreateValuesArray"], outputs: ["toggleAll"] }, { kind: "directive", type: XiriSelectDirective, selector: "[xiriSelect]", inputs: ["predicate", "compare", "serverSideSearch", "serverUrl", "serverParams", "values"], exportAs: ["xiriSelect"] }, { kind: "component", type: XiriTreeselectComponent, selector: "xiri-treeselect", inputs: ["field", "value"] }, { kind: "component", type: XiriDateComponent, selector: "xiri-date", inputs: ["field", "value"] }, { kind: "component", type: XiriYearMonthComponent, selector: "xiri-yearmonth", inputs: ["field", "value"] }, { kind: "component", type: XiriDateRangeComponent, selector: "xiri-date-range", inputs: ["field", "value"] }, { kind: "component", type: XiriDateTimeRangeComponent, selector: "xiri-date-time-range", inputs: ["field", "value"] }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "directive", type: MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: XiriVolumeComponent, selector: "xiri-volume", inputs: ["field", "value"] }, { kind: "component", type: XiriTimelimitComponent, selector: "xiri-timelimit", inputs: ["placeholder", "required", "disabled", "value", "texts"] }, { kind: "component", type: XiriFileComponent, selector: "xiri-file", inputs: ["value", "field"] }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: XiriChipsComponent, selector: "xiri-chips", inputs: ["field"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: SafehtmlPipe, name: "safeHtml" }] }); }
|
|
2899
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: XiriFormFieldsComponent, isStandalone: true, selector: "xiri-form-fields", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, display: { classPropertyName: "display", publicName: "display", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, check: { classPropertyName: "check", publicName: "check", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formChange: "formChange" }, ngImport: i0, template: "<form class=\"xrow dynamic-form\" [formGroup]=\"formGroup\" [class]=\"display()\">\n\t@for (field of displayFields(); track field.id) {\n\t\t@if (isFieldVisible(field)) {\n\t\t@switch (field.type) {\n\t\t\t@case ('text') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [type]=\"field.subtype || 'text'\"\n\t\t\t\t\t [autocomplete]=\"field.name\"\n\t\t\t\t\t [attr.minlength]=\"field.min\"\n\t\t\t\t\t [attr.maxlength]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t@if (field.textPrefix) {\n\t\t\t\t\t\t<span matTextPrefix=\"\">{{ field.textPrefix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.textSuffix) {\n\t\t\t\t\t\t<span matTextSuffix=\"\">{{ field.textSuffix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconPrefix) {\n\t\t\t\t\t\t<mat-icon matPrefix>{{ field.iconPrefix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconSuffix) {\n\t\t\t\t\t\t<mat-icon matSuffix>{{ field.iconSuffix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('number') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [autocomplete]=\"field.name || field.id\"\n\t\t\t\t\t [lang]=\"field.locale || ''\"\n\t\t\t\t\t [type]=\"field.type\"\n\t\t\t\t\t [attr.min]=\"field.min\"\n\t\t\t\t\t [attr.max]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t@if (field.textPrefix) {\n\t\t\t\t\t\t<span matTextPrefix=\"\">{{ field.textPrefix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.textSuffix) {\n\t\t\t\t\t\t<span matTextSuffix=\"\">{{ field.textSuffix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconPrefix) {\n\t\t\t\t\t\t<mat-icon matPrefix>{{ field.iconPrefix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconSuffix) {\n\t\t\t\t\t\t<mat-icon matSuffix>{{ field.iconSuffix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('password') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [type]=\"field.pwdhide ? 'password' : 'text'\"\n\t\t\t\t\t [autocomplete]=\"field.name\"\n\t\t\t\t\t [attr.minlength]=\"field.min\"\n\t\t\t\t\t [attr.maxlength]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t<button mat-icon-button matSuffix class=\"form-field-button\" (click)=\"field.pwdhide = !field.pwdhide\" [attr.aria-label]=\"'Hide password'\"\n\t\t\t\t\t [attr.aria-pressed]=\"field.pwdhide\">\n\t\t\t\t\t\t<mat-icon>{{ field.pwdhide ? 'visibility_off' : 'visibility' }}</mat-icon>\n\t\t\t\t\t</button>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('select') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t@if (field.search === false) {\n\t\t\t\t\t\t<mat-select [formControlName]=\"field.id\" [multiple]=\"field.multiple\" [required]=\"!!field.required\" panelClass=\"selectPanel\">\n\t\t\t\t\t\t\t@for (x of field.list; track x.id) {\n\t\t\t\t\t\t\t\t<mat-option [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</mat-select>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<mat-select [formControlName]=\"field.id\" [multiple]=\"field.multiple\" [required]=\"!!field.required\" panelClass=\"selectPanel\">\n\t\t\t\t\t\t\t<mat-option>\n\t\t\t\t\t\t\t\t<ngx-mat-select-search [formControl]=\"xiriSelect.formControl\" xiriSelect #xiriSelect=\"xiriSelect\"\n\t\t\t\t\t\t\t\t [values]=\"field.list || []\" [searching]=\"xiriSelect.searching\"\n\t\t\t\t\t\t\t\t [serverSideSearch]=\"!!field.serverSideSearch\" [serverUrl]=\"field.url || ''\"\n\t\t\t\t\t\t\t\t [serverParams]=\"field.params || {}\"></ngx-mat-select-search>\n\t\t\t\t\t\t\t</mat-option>\n\t\t\t\t\t\t\t@for (x of xiriSelect.filter | async; track x.id) {\n\t\t\t\t\t\t\t\t<mat-option [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</mat-select>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('treeselect') {\n\t\t\t\t<div class=\"treeselect\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-treeselect [field]=\"field\" [formControlName]=\"field.id\"\n\t\t\t\t\t [class.error]=\"formGroup.get(field.id)?.touched && formGroup.get(field.id)?.invalid\"></xiri-treeselect>\n\t\t\t\t\t<div class=\"xhint\" aria-live=\"polite\" [class.error]=\"formGroup.get(field.id)?.touched && formGroup.get(field.id)?.invalid\">\n\t\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.touched && formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('date') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"date\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date [field]=\"field\" [formControlName]=\"field.id\"></xiri-date>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('yearmonth') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"yearmonth\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-yearmonth [field]=\"field\" [formControlName]=\"field.id\"></xiri-yearmonth>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('daterange') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"daterange\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date-range [field]=\"field\" [formControlName]=\"field.id\"></xiri-date-range>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('datetimerange') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"datetimerange\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date-time-range [field]=\"field\" [formControlName]=\"field.id\"></xiri-date-time-range>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('bool') {\n\t\t\t\t<div class=\"bool\" [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-checkbox [formControlName]=\"field.id\">{{ field.name }}</mat-checkbox>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('radio') {\n\t\t\t\t<div class=\"radio\" [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<span class=\"radio-label\" [id]=\"field.id + '-label'\">{{ field.name }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t<mat-radio-group\n\t\t\t\t\t\t[formControlName]=\"field.id\"\n\t\t\t\t\t\t[attr.aria-labelledby]=\"field.name ? field.id + '-label' : null\"\n\t\t\t\t\t>\n\t\t\t\t\t\t@for (x of field.list; track x.id) {\n\t\t\t\t\t\t\t<mat-radio-button [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-radio-button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</mat-radio-group>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('volume') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-volume [field]=\"field\" [formControlName]=\"field.id\"></xiri-volume>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('timelimit') {\n\t\t\t\t<div [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-timelimit matInput [formControlName]=\"field.id\" [required]=\"!!field.required\" [texts]=\"field.texts || {}\"></xiri-timelimit>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('textarea') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<textarea matInput [formControlName]=\"field.id\" [required]=\"!!field.required\" [rows]=\"field.rows || 5\"></textarea>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('file') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-file [field]=\"field\" [formControlName]=\"field.id\"></xiri-file>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('info') {\n\t\t\t\t<div class=\"info\" [class]=\"field.class\">{{ field.value }}</div>\n\t\t\t}\n\t\t\t@case ('header') {\n\t\t\t\t@if (field.collapsible) {\n\t\t\t\t\t<div class=\"header collapsible\" [class]=\"field.class\" role=\"button\" tabindex=\"0\"\n\t\t\t\t\t [attr.aria-expanded]=\"!isSectionCollapsed(field.id)\"\n\t\t\t\t\t (click)=\"toggleSection(field)\" (keydown.enter)=\"toggleSection(field)\" (keydown.space)=\"toggleSection(field); $event.preventDefault()\">\n\t\t\t\t\t\t<mat-icon class=\"collapse-icon\" [class.collapsed]=\"isSectionCollapsed(field.id)\">expand_more</mat-icon>\n\t\t\t\t\t\t{{ field.value }}\n\t\t\t\t\t</div>\n\t\t\t\t} @else {\n\t\t\t\t\t<div class=\"header\" [class]=\"field.class\">{{ field.value }}</div>\n\t\t\t\t}\n\t\t\t}\n\t\t\t@case ('divider') {\n\t\t\t\t<div class=\"divider\" [class]=\"field.class\">\n\t\t\t\t\t@if (field.value) {\n\t\t\t\t\t\t<span class=\"divider-text\">{{ field.value }}</span>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('html') {\n\t\t\t\t<div class=\"html\" [class]=\"field.class\" [innerHtml]=\"$any(field.value) | safeHtml\"></div>\n\t\t\t}\n\t\t\t@case ('question') {\n\t\t\t\t<div class=\"xcol question\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<div class=\"left\">\n\t\t\t\t\t\t<mat-icon [class]=\"field.iconColor || 'warn'\" inline=\"true\">{{ field.icon }}</mat-icon>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"right\">{{ field.value }}</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('waiting') {\n\t\t\t\t<div class=\"xcol waiting\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<div class=\"left\">\n\t\t\t\t\t\t@if (!field.done) {\n\t\t\t\t\t\t\t<mat-spinner diameter=\"40\"></mat-spinner>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (field.done) {\n\t\t\t\t\t\t\t<mat-icon inline=\"true\">done</mat-icon>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"right\">{{ field.value }}</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('chips') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-chips [field]=\"field\" [formControlName]=\"field.id\"></xiri-chips>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@default {\n\t\t\t\t<div>default: {{ field.type }}</div>\n\t\t\t}\n\t\t}\n\t\t}\n\t}\n</form>\n", styles: [".dynamic-form{grid-row-gap:7px}.dynamic-form mat-form-field mat-icon{--mat-icon-color: var(--xiri-formfield-icon-color)}.dynamic-form.line{grid-row-gap:5px}.dynamic-form.small{grid-row-gap:2px}.dynamic-form textarea{white-space:pre}.dynamic-form .xhint{display:flex;min-height:20px;margin-top:4px}.dynamic-form .xhint mat-hint{font-size:var(--xiri-font-size-helper);padding:1px 16px 0}.dynamic-form .xhint mat-hint.mat-mdc-form-field-hint-end{flex:1;text-align:right}.dynamic-form .xhint mat-error{font-size:var(--xiri-font-size-helper);padding:1px 16px 0}.dynamic-form .xhint.error mat-hint{display:none}.dynamic-form .date{align-self:start;min-width:min(9.5rem,100%)}.dynamic-form .date.datetime{min-width:min(14.5rem,100%)}.dynamic-form .daterange{align-self:start;min-width:min(14.5rem,100%)}.dynamic-form .datetimerange{align-self:start;min-width:min(18.5rem,100%)}.dynamic-form .bool{display:flex;flex-direction:column}.dynamic-form .bool mat-hint{font-size:var(--xiri-font-size-helper);margin-top:-8px;padding-left:44px}.dynamic-form .radio{display:flex;flex-direction:column;gap:var(--xiri-spacing-xs)}.dynamic-form .radio .radio-label{font-size:var(--xiri-font-size-label)}.dynamic-form .radio mat-radio-group{display:flex;flex-wrap:wrap;gap:var(--xiri-spacing-xs) var(--xiri-spacing-md)}.dynamic-form .radio mat-hint{font-size:var(--xiri-font-size-helper)}.dynamic-form .info{padding-top:.8rem;padding-bottom:.4rem;color:var(--primary, #2892D9)}.dynamic-form .header{font-size:var(--xiri-font-size-title);padding-top:.8rem;padding-bottom:.4rem;color:var(--primary, #2892D9)}.dynamic-form .header.collapsible{cursor:pointer;display:flex;align-items:center;gap:.25rem;-webkit-user-select:none;user-select:none;border-radius:var(--xiri-radius-xs);padding:.5rem .25rem;margin:.25rem 0}.dynamic-form .header.collapsible:hover{background:color-mix(in srgb,var(--primary, #2892D9) 8%,transparent)}.dynamic-form .header.collapsible .collapse-icon{transition:transform var(--xiri-duration-fast) var(--xiri-easing-standard)}.dynamic-form .header.collapsible .collapse-icon.collapsed{transform:rotate(-90deg)}.dynamic-form .divider{width:100%;display:flex;align-items:center;padding:.5rem 0;margin:.25rem 0}.dynamic-form .divider:before,.dynamic-form .divider:after{content:\"\";flex:1;height:1px;background:var(--outline-variant, var(--mat-sys-outline-variant, rgba(0, 0, 0, .12)))}.dynamic-form .divider .divider-text{padding:0 1rem;font-size:var(--xiri-font-size-helper);color:var(--on-surface-variant, var(--mat-sys-on-surface-variant, rgba(0, 0, 0, .6)));white-space:nowrap}.dynamic-form .question,.dynamic-form .waiting{display:flex}.dynamic-form .question .left,.dynamic-form .waiting .left{font-size:var(--xiri-font-size-display);padding-right:1rem}.dynamic-form .question{align-items:center}.dynamic-form .question .left mat-icon{height:auto;width:auto;line-height:1;overflow:visible}.dynamic-form .form-field-button{margin-left:12px;margin-right:12px}.mat-mdc-select-panel.selectPanel{max-height:max(50vh,256px)}[hidden]{display:none!important}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: NgxMatSelectSearchModule }, { kind: "component", type: i2.MatSelectSearchComponent, selector: "ngx-mat-select-search", inputs: ["placeholderLabel", "type", "closeIcon", "closeSvgIcon", "noEntriesFoundLabel", "clearSearchInput", "searching", "disableInitialFocus", "enableClearOnEscapePressed", "preventHomeEndKeyPropagation", "disableScrollToActiveOnOptionsChanged", "ariaLabel", "showToggleAllCheckbox", "toggleAllCheckboxChecked", "toggleAllCheckboxIndeterminate", "toggleAllCheckboxTooltipMessage", "toggleAllCheckboxTooltipPosition", "hideClearSearchButton", "alwaysRestoreSelectedOptionsMulti", "recreateValuesArray"], outputs: ["toggleAll"] }, { kind: "directive", type: XiriSelectDirective, selector: "[xiriSelect]", inputs: ["predicate", "compare", "serverSideSearch", "serverUrl", "serverParams", "values"], exportAs: ["xiriSelect"] }, { kind: "component", type: XiriTreeselectComponent, selector: "xiri-treeselect", inputs: ["field", "value"] }, { kind: "component", type: XiriDateComponent, selector: "xiri-date", inputs: ["field", "value"] }, { kind: "component", type: XiriYearMonthComponent, selector: "xiri-yearmonth", inputs: ["field", "value"] }, { kind: "component", type: XiriDateRangeComponent, selector: "xiri-date-range", inputs: ["field", "value"] }, { kind: "component", type: XiriDateTimeRangeComponent, selector: "xiri-date-time-range", inputs: ["field", "value"] }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "directive", type: MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: XiriVolumeComponent, selector: "xiri-volume", inputs: ["field", "value"] }, { kind: "component", type: XiriTimelimitComponent, selector: "xiri-timelimit", inputs: ["placeholder", "required", "disabled", "value", "texts"] }, { kind: "component", type: XiriFileComponent, selector: "xiri-file", inputs: ["value", "field"] }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: XiriChipsComponent, selector: "xiri-chips", inputs: ["field"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: SafehtmlPipe, name: "safeHtml" }] }); }
|
|
2707
2900
|
}
|
|
2708
2901
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriFormFieldsComponent, decorators: [{
|
|
2709
2902
|
type: Component,
|
|
@@ -2738,8 +2931,106 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
2738
2931
|
AsyncPipe,
|
|
2739
2932
|
SafehtmlPipe,
|
|
2740
2933
|
XiriChipsComponent,
|
|
2741
|
-
], template: "<form class=\"xrow dynamic-form\" [formGroup]=\"formGroup\" [class]=\"display()\">\n\t@for (field of fields(); track field.id) {\n\t\t@if (isFieldVisible(field)) {\n\t\t@switch (field.type) {\n\t\t\t@case ('text') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [type]=\"field.subtype || 'text'\"\n\t\t\t\t\t [autocomplete]=\"field.name\"\n\t\t\t\t\t [attr.minlength]=\"field.min\"\n\t\t\t\t\t [attr.maxlength]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t@if (field.textPrefix) {\n\t\t\t\t\t\t<span matTextPrefix=\"\">{{ field.textPrefix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.textSuffix) {\n\t\t\t\t\t\t<span matTextSuffix=\"\">{{ field.textSuffix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconPrefix) {\n\t\t\t\t\t\t<mat-icon matPrefix>{{ field.iconPrefix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconSuffix) {\n\t\t\t\t\t\t<mat-icon matSuffix>{{ field.iconSuffix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('number') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [autocomplete]=\"field.name || field.id\"\n\t\t\t\t\t [lang]=\"field.locale || ''\"\n\t\t\t\t\t [type]=\"field.type\"\n\t\t\t\t\t [attr.min]=\"field.min\"\n\t\t\t\t\t [attr.max]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t@if (field.textPrefix) {\n\t\t\t\t\t\t<span matTextPrefix=\"\">{{ field.textPrefix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.textSuffix) {\n\t\t\t\t\t\t<span matTextSuffix=\"\">{{ field.textSuffix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconPrefix) {\n\t\t\t\t\t\t<mat-icon matPrefix>{{ field.iconPrefix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconSuffix) {\n\t\t\t\t\t\t<mat-icon matSuffix>{{ field.iconSuffix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('password') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [type]=\"field.pwdhide ? 'password' : 'text'\"\n\t\t\t\t\t [autocomplete]=\"field.name\"\n\t\t\t\t\t [attr.minlength]=\"field.min\"\n\t\t\t\t\t [attr.maxlength]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t<button mat-icon-button matSuffix class=\"form-field-button\" (click)=\"field.pwdhide = !field.pwdhide\" [attr.aria-label]=\"'Hide password'\"\n\t\t\t\t\t [attr.aria-pressed]=\"field.pwdhide\">\n\t\t\t\t\t\t<mat-icon>{{ field.pwdhide ? 'visibility_off' : 'visibility' }}</mat-icon>\n\t\t\t\t\t</button>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('select') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t@if (field.search === false) {\n\t\t\t\t\t\t<mat-select [formControlName]=\"field.id\" [multiple]=\"field.multiple\" [required]=\"!!field.required\" panelClass=\"selectPanel\">\n\t\t\t\t\t\t\t@for (x of field.list; track x.id) {\n\t\t\t\t\t\t\t\t<mat-option [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</mat-select>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<mat-select [formControlName]=\"field.id\" [multiple]=\"field.multiple\" [required]=\"!!field.required\" panelClass=\"selectPanel\">\n\t\t\t\t\t\t\t<mat-option>\n\t\t\t\t\t\t\t\t<ngx-mat-select-search [formControl]=\"xiriSelect.formControl\" xiriSelect #xiriSelect=\"xiriSelect\"\n\t\t\t\t\t\t\t\t [values]=\"field.list || []\" [searching]=\"xiriSelect.searching\"\n\t\t\t\t\t\t\t\t [serverSideSearch]=\"!!field.serverSideSearch\" [serverUrl]=\"field.url || ''\"\n\t\t\t\t\t\t\t\t [serverParams]=\"field.params || {}\"></ngx-mat-select-search>\n\t\t\t\t\t\t\t</mat-option>\n\t\t\t\t\t\t\t@for (x of xiriSelect.filter | async; track x.id) {\n\t\t\t\t\t\t\t\t<mat-option [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</mat-select>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('treeselect') {\n\t\t\t\t<div class=\"treeselect\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-treeselect [field]=\"field\" [formControlName]=\"field.id\"\n\t\t\t\t\t [class.error]=\"formGroup.get(field.id)?.touched && formGroup.get(field.id)?.invalid\"></xiri-treeselect>\n\t\t\t\t\t<div class=\"xhint\" aria-live=\"polite\" [class.error]=\"formGroup.get(field.id)?.touched && formGroup.get(field.id)?.invalid\">\n\t\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.touched && formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('date') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"date\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date [field]=\"field\" [formControlName]=\"field.id\"></xiri-date>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('yearmonth') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"yearmonth\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-yearmonth [field]=\"field\" [formControlName]=\"field.id\"></xiri-yearmonth>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('daterange') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"daterange\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date-range [field]=\"field\" [formControlName]=\"field.id\"></xiri-date-range>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('datetimerange') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"datetimerange\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date-time-range [field]=\"field\" [formControlName]=\"field.id\"></xiri-date-time-range>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('bool') {\n\t\t\t\t<div class=\"bool\" [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-checkbox [formControlName]=\"field.id\">{{ field.name }}</mat-checkbox>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('radio') {\n\t\t\t\t<div class=\"radio\" [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<span class=\"radio-label\" [id]=\"field.id + '-label'\">{{ field.name }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t<mat-radio-group\n\t\t\t\t\t\t[formControlName]=\"field.id\"\n\t\t\t\t\t\t[attr.aria-labelledby]=\"field.name ? field.id + '-label' : null\"\n\t\t\t\t\t>\n\t\t\t\t\t\t@for (x of field.list; track x.id) {\n\t\t\t\t\t\t\t<mat-radio-button [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-radio-button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</mat-radio-group>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('volume') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-volume [field]=\"field\" [formControlName]=\"field.id\"></xiri-volume>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('timelimit') {\n\t\t\t\t<div [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-timelimit matInput [formControlName]=\"field.id\" [required]=\"!!field.required\" [texts]=\"field.texts || {}\"></xiri-timelimit>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('textarea') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<textarea matInput [formControlName]=\"field.id\" [required]=\"!!field.required\" [rows]=\"field.rows || 5\"></textarea>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('file') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-file [field]=\"field\" [formControlName]=\"field.id\"></xiri-file>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('info') {\n\t\t\t\t<div class=\"info\" [class]=\"field.class\">{{ field.value }}</div>\n\t\t\t}\n\t\t\t@case ('header') {\n\t\t\t\t@if (field.collapsible) {\n\t\t\t\t\t<div class=\"header collapsible\" [class]=\"field.class\" role=\"button\" tabindex=\"0\"\n\t\t\t\t\t [attr.aria-expanded]=\"!isSectionCollapsed(field.id)\"\n\t\t\t\t\t (click)=\"toggleSection(field)\" (keydown.enter)=\"toggleSection(field)\" (keydown.space)=\"toggleSection(field); $event.preventDefault()\">\n\t\t\t\t\t\t<mat-icon class=\"collapse-icon\" [class.collapsed]=\"isSectionCollapsed(field.id)\">expand_more</mat-icon>\n\t\t\t\t\t\t{{ field.value }}\n\t\t\t\t\t</div>\n\t\t\t\t} @else {\n\t\t\t\t\t<div class=\"header\" [class]=\"field.class\">{{ field.value }}</div>\n\t\t\t\t}\n\t\t\t}\n\t\t\t@case ('divider') {\n\t\t\t\t<div class=\"divider\" [class]=\"field.class\">\n\t\t\t\t\t@if (field.value) {\n\t\t\t\t\t\t<span class=\"divider-text\">{{ field.value }}</span>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('html') {\n\t\t\t\t<div class=\"html\" [class]=\"field.class\" [innerHtml]=\"$any(field.value) | safeHtml\"></div>\n\t\t\t}\n\t\t\t@case ('question') {\n\t\t\t\t<div class=\"xcol question\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<div class=\"left\">\n\t\t\t\t\t\t<mat-icon [class]=\"field.iconColor || 'warn'\" inline=\"true\">{{ field.icon }}</mat-icon>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"right\">{{ field.value }}</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('waiting') {\n\t\t\t\t<div class=\"xcol waiting\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<div class=\"left\">\n\t\t\t\t\t\t@if (!field.done) {\n\t\t\t\t\t\t\t<mat-spinner diameter=\"40\"></mat-spinner>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (field.done) {\n\t\t\t\t\t\t\t<mat-icon inline=\"true\">done</mat-icon>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"right\">{{ field.value }}</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('chips') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-chips [field]=\"field\" [formControlName]=\"field.id\"></xiri-chips>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@default {\n\t\t\t\t<div>default: {{ field.type }}</div>\n\t\t\t}\n\t\t}\n\t\t}\n\t}\n</form>\n", styles: [".dynamic-form{grid-row-gap:7px}.dynamic-form mat-form-field mat-icon{--mat-icon-color: var(--xiri-formfield-icon-color)}.dynamic-form.line{grid-row-gap:5px}.dynamic-form.small{grid-row-gap:2px}.dynamic-form textarea{white-space:pre}.dynamic-form .xhint{display:flex;min-height:20px;margin-top:4px}.dynamic-form .xhint mat-hint{font-size:var(--xiri-font-size-helper);padding:1px 16px 0}.dynamic-form .xhint mat-hint.mat-mdc-form-field-hint-end{flex:1;text-align:right}.dynamic-form .xhint mat-error{font-size:var(--xiri-font-size-helper);padding:1px 16px 0}.dynamic-form .xhint.error mat-hint{display:none}.dynamic-form .date{align-self:start;min-width:min(9.5rem,100%)}.dynamic-form .date.datetime{min-width:min(14.5rem,100%)}.dynamic-form .daterange{align-self:start;min-width:min(14.5rem,100%)}.dynamic-form .datetimerange{align-self:start;min-width:min(18.5rem,100%)}.dynamic-form .bool{display:flex;flex-direction:column}.dynamic-form .bool mat-hint{font-size:var(--xiri-font-size-helper);margin-top:-8px;padding-left:44px}.dynamic-form .radio{display:flex;flex-direction:column;gap:var(--xiri-spacing-xs)}.dynamic-form .radio .radio-label{font-size:var(--xiri-font-size-label)}.dynamic-form .radio mat-radio-group{display:flex;flex-wrap:wrap;gap:var(--xiri-spacing-xs) var(--xiri-spacing-md)}.dynamic-form .radio mat-hint{font-size:var(--xiri-font-size-helper)}.dynamic-form .info{padding-top:.8rem;padding-bottom:.4rem;color:var(--primary, #2892D9)}.dynamic-form .header{font-size:var(--xiri-font-size-title);padding-top:.8rem;padding-bottom:.4rem;color:var(--primary, #2892D9)}.dynamic-form .header.collapsible{cursor:pointer;display:flex;align-items:center;gap:.25rem;-webkit-user-select:none;user-select:none;border-radius:var(--xiri-radius-xs);padding:.5rem .25rem;margin:.25rem 0}.dynamic-form .header.collapsible:hover{background:color-mix(in srgb,var(--primary, #2892D9) 8%,transparent)}.dynamic-form .header.collapsible .collapse-icon{transition:transform var(--xiri-duration-fast) var(--xiri-easing-standard)}.dynamic-form .header.collapsible .collapse-icon.collapsed{transform:rotate(-90deg)}.dynamic-form .divider{width:100%;display:flex;align-items:center;padding:.5rem 0;margin:.25rem 0}.dynamic-form .divider:before,.dynamic-form .divider:after{content:\"\";flex:1;height:1px;background:var(--outline-variant, var(--mat-sys-outline-variant, rgba(0, 0, 0, .12)))}.dynamic-form .divider .divider-text{padding:0 1rem;font-size:var(--xiri-font-size-helper);color:var(--on-surface-variant, var(--mat-sys-on-surface-variant, rgba(0, 0, 0, .6)));white-space:nowrap}.dynamic-form .question,.dynamic-form .waiting{display:flex}.dynamic-form .question .left,.dynamic-form .waiting .left{font-size:var(--xiri-font-size-display);padding-right:1rem}.dynamic-form .question{align-items:center}.dynamic-form .question .left mat-icon{height:auto;width:auto;line-height:1;overflow:visible}.dynamic-form .form-field-button{margin-left:12px;margin-right:12px}.mat-mdc-select-panel.selectPanel{max-height:max(50vh,256px)}[hidden]{display:none!important}\n"] }]
|
|
2934
|
+
], template: "<form class=\"xrow dynamic-form\" [formGroup]=\"formGroup\" [class]=\"display()\">\n\t@for (field of displayFields(); track field.id) {\n\t\t@if (isFieldVisible(field)) {\n\t\t@switch (field.type) {\n\t\t\t@case ('text') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [type]=\"field.subtype || 'text'\"\n\t\t\t\t\t [autocomplete]=\"field.name\"\n\t\t\t\t\t [attr.minlength]=\"field.min\"\n\t\t\t\t\t [attr.maxlength]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t@if (field.textPrefix) {\n\t\t\t\t\t\t<span matTextPrefix=\"\">{{ field.textPrefix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.textSuffix) {\n\t\t\t\t\t\t<span matTextSuffix=\"\">{{ field.textSuffix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconPrefix) {\n\t\t\t\t\t\t<mat-icon matPrefix>{{ field.iconPrefix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconSuffix) {\n\t\t\t\t\t\t<mat-icon matSuffix>{{ field.iconSuffix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('number') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [autocomplete]=\"field.name || field.id\"\n\t\t\t\t\t [lang]=\"field.locale || ''\"\n\t\t\t\t\t [type]=\"field.type\"\n\t\t\t\t\t [attr.min]=\"field.min\"\n\t\t\t\t\t [attr.max]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t@if (field.textPrefix) {\n\t\t\t\t\t\t<span matTextPrefix=\"\">{{ field.textPrefix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.textSuffix) {\n\t\t\t\t\t\t<span matTextSuffix=\"\">{{ field.textSuffix }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconPrefix) {\n\t\t\t\t\t\t<mat-icon matPrefix>{{ field.iconPrefix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.iconSuffix) {\n\t\t\t\t\t\t<mat-icon matSuffix>{{ field.iconSuffix }}</mat-icon>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('password') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<input matInput\n\t\t\t\t\t [formControlName]=\"field.id\"\n\t\t\t\t\t [type]=\"field.pwdhide ? 'password' : 'text'\"\n\t\t\t\t\t [autocomplete]=\"field.name\"\n\t\t\t\t\t [attr.minlength]=\"field.min\"\n\t\t\t\t\t [attr.maxlength]=\"field.max\"\n\t\t\t\t\t [required]=\"!!field.required\">\n\t\t\t\t\t<button mat-icon-button matSuffix class=\"form-field-button\" (click)=\"field.pwdhide = !field.pwdhide\" [attr.aria-label]=\"'Hide password'\"\n\t\t\t\t\t [attr.aria-pressed]=\"field.pwdhide\">\n\t\t\t\t\t\t<mat-icon>{{ field.pwdhide ? 'visibility_off' : 'visibility' }}</mat-icon>\n\t\t\t\t\t</button>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('select') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" floatLabel=\"auto\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t@if (field.search === false) {\n\t\t\t\t\t\t<mat-select [formControlName]=\"field.id\" [multiple]=\"field.multiple\" [required]=\"!!field.required\" panelClass=\"selectPanel\">\n\t\t\t\t\t\t\t@for (x of field.list; track x.id) {\n\t\t\t\t\t\t\t\t<mat-option [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</mat-select>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<mat-select [formControlName]=\"field.id\" [multiple]=\"field.multiple\" [required]=\"!!field.required\" panelClass=\"selectPanel\">\n\t\t\t\t\t\t\t<mat-option>\n\t\t\t\t\t\t\t\t<ngx-mat-select-search [formControl]=\"xiriSelect.formControl\" xiriSelect #xiriSelect=\"xiriSelect\"\n\t\t\t\t\t\t\t\t [values]=\"field.list || []\" [searching]=\"xiriSelect.searching\"\n\t\t\t\t\t\t\t\t [serverSideSearch]=\"!!field.serverSideSearch\" [serverUrl]=\"field.url || ''\"\n\t\t\t\t\t\t\t\t [serverParams]=\"field.params || {}\"></ngx-mat-select-search>\n\t\t\t\t\t\t\t</mat-option>\n\t\t\t\t\t\t\t@for (x of xiriSelect.filter | async; track x.id) {\n\t\t\t\t\t\t\t\t<mat-option [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</mat-select>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('treeselect') {\n\t\t\t\t<div class=\"treeselect\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-treeselect [field]=\"field\" [formControlName]=\"field.id\"\n\t\t\t\t\t [class.error]=\"formGroup.get(field.id)?.touched && formGroup.get(field.id)?.invalid\"></xiri-treeselect>\n\t\t\t\t\t<div class=\"xhint\" aria-live=\"polite\" [class.error]=\"formGroup.get(field.id)?.touched && formGroup.get(field.id)?.invalid\">\n\t\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.touched && formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('date') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"date\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date [field]=\"field\" [formControlName]=\"field.id\"></xiri-date>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('yearmonth') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"yearmonth\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-yearmonth [field]=\"field\" [formControlName]=\"field.id\"></xiri-yearmonth>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('daterange') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"daterange\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date-range [field]=\"field\" [formControlName]=\"field.id\"></xiri-date-range>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('datetimerange') {\n\t\t\t\t<mat-form-field [formGroup]=\"formGroup\" class=\"datetimerange\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t<xiri-date-time-range [field]=\"field\" [formControlName]=\"field.id\"></xiri-date-time-range>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('bool') {\n\t\t\t\t<div class=\"bool\" [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t<mat-checkbox [formControlName]=\"field.id\">{{ field.name }}</mat-checkbox>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('radio') {\n\t\t\t\t<div class=\"radio\" [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<span class=\"radio-label\" [id]=\"field.id + '-label'\">{{ field.name }}</span>\n\t\t\t\t\t}\n\t\t\t\t\t<mat-radio-group\n\t\t\t\t\t\t[formControlName]=\"field.id\"\n\t\t\t\t\t\t[attr.aria-labelledby]=\"field.name ? field.id + '-label' : null\"\n\t\t\t\t\t>\n\t\t\t\t\t\t@for (x of field.list; track x.id) {\n\t\t\t\t\t\t\t<mat-radio-button [value]=\"x.id\" [disabled]=\"x.disabled\">{{ x.name }}</mat-radio-button>\n\t\t\t\t\t\t}\n\t\t\t\t\t</mat-radio-group>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('volume') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-volume [field]=\"field\" [formControlName]=\"field.id\"></xiri-volume>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('timelimit') {\n\t\t\t\t<div [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-timelimit matInput [formControlName]=\"field.id\" [required]=\"!!field.required\" [texts]=\"field.texts || {}\"></xiri-timelimit>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('textarea') {\n\t\t\t\t<mat-form-field [class]=\"field.class\" [formGroup]=\"formGroup\" [hidden]=\"field.hide\">\n\t\t\t\t\t@if (field.name) {\n\t\t\t\t\t\t<mat-label>{{ field.name }}</mat-label>\n\t\t\t\t\t}\n\t\t\t\t\t<textarea matInput [formControlName]=\"field.id\" [required]=\"!!field.required\" [rows]=\"field.rows || 5\"></textarea>\n\t\t\t\t\t@if (field.hint) {\n\t\t\t\t\t\t<mat-hint>{{ field.hint }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@if (field.max) {\n\t\t\t\t\t\t<mat-hint align=\"end\">{{ formGroup.get(field.id)?.value?.length || 0 }} / {{ field.max }}</mat-hint>\n\t\t\t\t\t}\n\t\t\t\t\t@for (vali of field.validations; track vali.id) {\n\t\t\t\t\t\t<ng-container ngProjectAs=\"mat-error\">\n\t\t\t\t\t\t\t@if (formGroup.get(field.id)?.hasError(vali.id)) {\n\t\t\t\t\t\t\t\t<mat-error>{{ vali.message }}</mat-error>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t}\n\t\t\t\t</mat-form-field>\n\t\t\t}\n\t\t\t@case ('file') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-file [field]=\"field\" [formControlName]=\"field.id\"></xiri-file>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('info') {\n\t\t\t\t<div class=\"info\" [class]=\"field.class\">{{ field.value }}</div>\n\t\t\t}\n\t\t\t@case ('header') {\n\t\t\t\t@if (field.collapsible) {\n\t\t\t\t\t<div class=\"header collapsible\" [class]=\"field.class\" role=\"button\" tabindex=\"0\"\n\t\t\t\t\t [attr.aria-expanded]=\"!isSectionCollapsed(field.id)\"\n\t\t\t\t\t (click)=\"toggleSection(field)\" (keydown.enter)=\"toggleSection(field)\" (keydown.space)=\"toggleSection(field); $event.preventDefault()\">\n\t\t\t\t\t\t<mat-icon class=\"collapse-icon\" [class.collapsed]=\"isSectionCollapsed(field.id)\">expand_more</mat-icon>\n\t\t\t\t\t\t{{ field.value }}\n\t\t\t\t\t</div>\n\t\t\t\t} @else {\n\t\t\t\t\t<div class=\"header\" [class]=\"field.class\">{{ field.value }}</div>\n\t\t\t\t}\n\t\t\t}\n\t\t\t@case ('divider') {\n\t\t\t\t<div class=\"divider\" [class]=\"field.class\">\n\t\t\t\t\t@if (field.value) {\n\t\t\t\t\t\t<span class=\"divider-text\">{{ field.value }}</span>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('html') {\n\t\t\t\t<div class=\"html\" [class]=\"field.class\" [innerHtml]=\"$any(field.value) | safeHtml\"></div>\n\t\t\t}\n\t\t\t@case ('question') {\n\t\t\t\t<div class=\"xcol question\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<div class=\"left\">\n\t\t\t\t\t\t<mat-icon [class]=\"field.iconColor || 'warn'\" inline=\"true\">{{ field.icon }}</mat-icon>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"right\">{{ field.value }}</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('waiting') {\n\t\t\t\t<div class=\"xcol waiting\" [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<div class=\"left\">\n\t\t\t\t\t\t@if (!field.done) {\n\t\t\t\t\t\t\t<mat-spinner diameter=\"40\"></mat-spinner>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t@if (field.done) {\n\t\t\t\t\t\t\t<mat-icon inline=\"true\">done</mat-icon>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"right\">{{ field.value }}</div>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@case ('chips') {\n\t\t\t\t<div [class]=\"field.class\" [hidden]=\"field.hide\">\n\t\t\t\t\t<xiri-chips [field]=\"field\" [formControlName]=\"field.id\"></xiri-chips>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@default {\n\t\t\t\t<div>default: {{ field.type }}</div>\n\t\t\t}\n\t\t}\n\t\t}\n\t}\n</form>\n", styles: [".dynamic-form{grid-row-gap:7px}.dynamic-form mat-form-field mat-icon{--mat-icon-color: var(--xiri-formfield-icon-color)}.dynamic-form.line{grid-row-gap:5px}.dynamic-form.small{grid-row-gap:2px}.dynamic-form textarea{white-space:pre}.dynamic-form .xhint{display:flex;min-height:20px;margin-top:4px}.dynamic-form .xhint mat-hint{font-size:var(--xiri-font-size-helper);padding:1px 16px 0}.dynamic-form .xhint mat-hint.mat-mdc-form-field-hint-end{flex:1;text-align:right}.dynamic-form .xhint mat-error{font-size:var(--xiri-font-size-helper);padding:1px 16px 0}.dynamic-form .xhint.error mat-hint{display:none}.dynamic-form .date{align-self:start;min-width:min(9.5rem,100%)}.dynamic-form .date.datetime{min-width:min(14.5rem,100%)}.dynamic-form .daterange{align-self:start;min-width:min(14.5rem,100%)}.dynamic-form .datetimerange{align-self:start;min-width:min(18.5rem,100%)}.dynamic-form .bool{display:flex;flex-direction:column}.dynamic-form .bool mat-hint{font-size:var(--xiri-font-size-helper);margin-top:-8px;padding-left:44px}.dynamic-form .radio{display:flex;flex-direction:column;gap:var(--xiri-spacing-xs)}.dynamic-form .radio .radio-label{font-size:var(--xiri-font-size-label)}.dynamic-form .radio mat-radio-group{display:flex;flex-wrap:wrap;gap:var(--xiri-spacing-xs) var(--xiri-spacing-md)}.dynamic-form .radio mat-hint{font-size:var(--xiri-font-size-helper)}.dynamic-form .info{padding-top:.8rem;padding-bottom:.4rem;color:var(--primary, #2892D9)}.dynamic-form .header{font-size:var(--xiri-font-size-title);padding-top:.8rem;padding-bottom:.4rem;color:var(--primary, #2892D9)}.dynamic-form .header.collapsible{cursor:pointer;display:flex;align-items:center;gap:.25rem;-webkit-user-select:none;user-select:none;border-radius:var(--xiri-radius-xs);padding:.5rem .25rem;margin:.25rem 0}.dynamic-form .header.collapsible:hover{background:color-mix(in srgb,var(--primary, #2892D9) 8%,transparent)}.dynamic-form .header.collapsible .collapse-icon{transition:transform var(--xiri-duration-fast) var(--xiri-easing-standard)}.dynamic-form .header.collapsible .collapse-icon.collapsed{transform:rotate(-90deg)}.dynamic-form .divider{width:100%;display:flex;align-items:center;padding:.5rem 0;margin:.25rem 0}.dynamic-form .divider:before,.dynamic-form .divider:after{content:\"\";flex:1;height:1px;background:var(--outline-variant, var(--mat-sys-outline-variant, rgba(0, 0, 0, .12)))}.dynamic-form .divider .divider-text{padding:0 1rem;font-size:var(--xiri-font-size-helper);color:var(--on-surface-variant, var(--mat-sys-on-surface-variant, rgba(0, 0, 0, .6)));white-space:nowrap}.dynamic-form .question,.dynamic-form .waiting{display:flex}.dynamic-form .question .left,.dynamic-form .waiting .left{font-size:var(--xiri-font-size-display);padding-right:1rem}.dynamic-form .question{align-items:center}.dynamic-form .question .left mat-icon{height:auto;width:auto;line-height:1;overflow:visible}.dynamic-form .form-field-button{margin-left:12px;margin-right:12px}.mat-mdc-select-panel.selectPanel{max-height:max(50vh,256px)}[hidden]{display:none!important}\n"] }]
|
|
2742
2935
|
}], ctorParameters: () => [], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], display: [{ type: i0.Input, args: [{ isSignal: true, alias: "display", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], formChange: [{ type: i0.Output, args: ["formChange"] }], check: [{ type: i0.Input, args: [{ isSignal: true, alias: "check", required: false }] }] } });
|
|
2936
|
+
// Was ein Reload-Patch am Feld ändern darf, jeweils mit Typprüfung. Der Server ist eine
|
|
2937
|
+
// Trust-Boundary: eine kaputte `list` oder ein `min: "3"` dürfen weder das Pruning noch den
|
|
2938
|
+
// Validator-Aufbau verfälschen.
|
|
2939
|
+
//
|
|
2940
|
+
// Bewusst nicht patchbar: `id`, `type`, `subtype` (createControl normalisiert die einmalig, ein
|
|
2941
|
+
// Roh-Wert vom Server würde das zerlegen), `value` (den Wert behält der Client, siehe pruneValue),
|
|
2942
|
+
// `control`, `showWhen` und `url` (`serverSideSearch` und der Suchfluss der Select-Directive
|
|
2943
|
+
// werden nur beim Aufbau abgeleitet, eine Änderung käme nie an).
|
|
2944
|
+
const PATCH_GUARDS = {
|
|
2945
|
+
list: isOptionList,
|
|
2946
|
+
name: isString,
|
|
2947
|
+
// Go exportiert einen leeren Hint als null - sonst liesse er sich nie wieder abräumen.
|
|
2948
|
+
hint: isStringOrNull,
|
|
2949
|
+
class: isString,
|
|
2950
|
+
required: isBoolean,
|
|
2951
|
+
disabled: isBoolean,
|
|
2952
|
+
hide: isBoolean,
|
|
2953
|
+
search: isBoolean,
|
|
2954
|
+
min: isNumber,
|
|
2955
|
+
max: isNumber,
|
|
2956
|
+
params: isPlainObject,
|
|
2957
|
+
};
|
|
2958
|
+
function isString(value) {
|
|
2959
|
+
return typeof value === 'string';
|
|
2960
|
+
}
|
|
2961
|
+
function isStringOrNull(value) {
|
|
2962
|
+
return value === null || typeof value === 'string';
|
|
2963
|
+
}
|
|
2964
|
+
function isBoolean(value) {
|
|
2965
|
+
return typeof value === 'boolean';
|
|
2966
|
+
}
|
|
2967
|
+
function isNumber(value) {
|
|
2968
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
2969
|
+
}
|
|
2970
|
+
function isPlainObject(value) {
|
|
2971
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2972
|
+
}
|
|
2973
|
+
// Rekursiv, inklusive children: eine Option mit einem children-Feld, das kein Array ist, würde
|
|
2974
|
+
// sonst erst später beim Sammeln der IDs oder beim Aufbau des Baums knallen.
|
|
2975
|
+
function isOptionList(value) {
|
|
2976
|
+
return Array.isArray(value) && value.every(isOption);
|
|
2977
|
+
}
|
|
2978
|
+
function isOption(value) {
|
|
2979
|
+
if (!isPlainObject(value))
|
|
2980
|
+
return false;
|
|
2981
|
+
const option = value;
|
|
2982
|
+
if (typeof option.id !== 'string' && typeof option.id !== 'number')
|
|
2983
|
+
return false;
|
|
2984
|
+
return option.children === undefined || option.children === null || isOptionList(option.children);
|
|
2985
|
+
}
|
|
2986
|
+
// Schreibt die erlaubten Properties ins Feld und meldet, welche davon wirklich etwas geändert
|
|
2987
|
+
// haben. Ein Reload liefert oft dasselbe zurück - z.B. weil ein anderer Trigger sich geändert
|
|
2988
|
+
// hat. Unveränderte Properties zu überspringen erspart den Klon (der einen Treeselect neu
|
|
2989
|
+
// aufbauen und bei gesetzter url erneut laden würde), den Validator-Neubau und den formChange.
|
|
2990
|
+
//
|
|
2991
|
+
// Verglichen wird bevorzugt mit dem zuletzt angewendeten Patch, nicht mit dem Feld: der
|
|
2992
|
+
// Treeselect hängt beim Aufbau parent-Referenzen und state an die Optionsknoten, wodurch die
|
|
2993
|
+
// Liste am Feld verschachtelt zyklisch wird und ohnehin nie mehr gleich aussieht.
|
|
2994
|
+
function applyPatchProperties(field, entry, previous) {
|
|
2995
|
+
const applied = [];
|
|
2996
|
+
const target = field;
|
|
2997
|
+
for (const key of Object.keys(PATCH_GUARDS)) {
|
|
2998
|
+
if (!(key in entry))
|
|
2999
|
+
continue;
|
|
3000
|
+
const value = entry[key];
|
|
3001
|
+
if (!PATCH_GUARDS[key](value))
|
|
3002
|
+
continue;
|
|
3003
|
+
const before = previous && key in previous ? previous[key] : target[key];
|
|
3004
|
+
if (sameValue(before, value))
|
|
3005
|
+
continue;
|
|
3006
|
+
target[key] = value === null ? undefined : value;
|
|
3007
|
+
applied.push(key);
|
|
3008
|
+
}
|
|
3009
|
+
return applied;
|
|
3010
|
+
}
|
|
3011
|
+
function sameValue(a, b) {
|
|
3012
|
+
if (a === b)
|
|
3013
|
+
return true;
|
|
3014
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
|
|
3015
|
+
return false;
|
|
3016
|
+
try {
|
|
3017
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
3018
|
+
}
|
|
3019
|
+
catch {
|
|
3020
|
+
// Zyklisch (siehe oben) - dann lieber anwenden als fälschlich überspringen.
|
|
3021
|
+
return false;
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
// Option-IDs inklusive verschachtelter children - eine flache Sammlung würde jede ausgewählte
|
|
3025
|
+
// Leaf-ID eines Treeselects verwerfen.
|
|
3026
|
+
function collectOptionIds(list, into = new Set()) {
|
|
3027
|
+
for (const option of list) {
|
|
3028
|
+
into.add(option.id);
|
|
3029
|
+
if (option.children?.length)
|
|
3030
|
+
collectOptionIds(option.children, into);
|
|
3031
|
+
}
|
|
3032
|
+
return into;
|
|
3033
|
+
}
|
|
2743
3034
|
function fillListFromArray(field) {
|
|
2744
3035
|
if (field.list === undefined && field.array !== undefined) {
|
|
2745
3036
|
const list = [];
|
|
@@ -2957,18 +3248,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
2957
3248
|
}]
|
|
2958
3249
|
}] });
|
|
2959
3250
|
|
|
2960
|
-
function parseHttpError(err) {
|
|
2961
|
-
if (err.status === 400 || err.status === 424)
|
|
2962
|
-
return err.error?.error || 'Format Error';
|
|
2963
|
-
if (err.status === 403)
|
|
2964
|
-
return err.error?.error || 'Access denied';
|
|
2965
|
-
if (err.status === 404)
|
|
2966
|
-
return 'Not found';
|
|
2967
|
-
if (err.status === 0)
|
|
2968
|
-
return 'Connection error';
|
|
2969
|
-
return 'Unknown error';
|
|
2970
|
-
}
|
|
2971
|
-
|
|
2972
3251
|
const DIALOG_SIZE_MAP = {
|
|
2973
3252
|
sm: '400px',
|
|
2974
3253
|
md: '600px',
|
|
@@ -5176,6 +5455,10 @@ class XiriTableComponent {
|
|
|
5176
5455
|
const column = this.displayedColumns.find(col => col.id === sortHeaderId);
|
|
5177
5456
|
if (column && column.format === 'number')
|
|
5178
5457
|
return data[sortHeaderId][1];
|
|
5458
|
+
// ponytail: sort by the first chip's label — without this MatTable compares the raw object
|
|
5459
|
+
// array, which coerces to "[object Object],..." and effectively sorts by chip count.
|
|
5460
|
+
if (column && column.format === 'chips')
|
|
5461
|
+
return data[sortHeaderId]?.[0]?.label ?? '';
|
|
5179
5462
|
return data[sortHeaderId];
|
|
5180
5463
|
};
|
|
5181
5464
|
}
|
|
@@ -5772,6 +6055,7 @@ class XiriQueryComponent {
|
|
|
5772
6055
|
constructor() {
|
|
5773
6056
|
this.dataService = inject(XiriDataService);
|
|
5774
6057
|
this.formService = inject(XiriFormService);
|
|
6058
|
+
this.storageService = inject(XiriSessionStorageService);
|
|
5775
6059
|
this.cdr = inject(ChangeDetectorRef);
|
|
5776
6060
|
this.destroyRef = inject(DestroyRef);
|
|
5777
6061
|
this.settings = input.required(/* @ts-ignore */
|
|
@@ -5783,11 +6067,15 @@ class XiriQueryComponent {
|
|
|
5783
6067
|
...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
|
|
5784
6068
|
this.filterChange = output();
|
|
5785
6069
|
this.waiter = new Subject();
|
|
6070
|
+
this.loader = new Subject();
|
|
5786
6071
|
this.dynData = { data: null, filterData: null };
|
|
5787
6072
|
this.saveState = false;
|
|
5788
6073
|
this.saveStateId = null;
|
|
5789
6074
|
this.extra = null;
|
|
5790
6075
|
this._initialChangeDone = false;
|
|
6076
|
+
// Expansion-panel state; undefined = no panel at all, the filter is always open.
|
|
6077
|
+
this.collapsed = signal(undefined, /* @ts-ignore */
|
|
6078
|
+
...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
5791
6079
|
this.data = signal(null, /* @ts-ignore */
|
|
5792
6080
|
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
5793
6081
|
this.error = signal(null, /* @ts-ignore */
|
|
@@ -5829,12 +6117,17 @@ class XiriQueryComponent {
|
|
|
5829
6117
|
get effectiveSaveKey() {
|
|
5830
6118
|
return this.saveStateId ? this.saveStateId + ':filter' : null;
|
|
5831
6119
|
}
|
|
6120
|
+
get collapsedSaveKey() {
|
|
6121
|
+
return this.saveStateId ? this.saveStateId + ':collapsed' : null;
|
|
6122
|
+
}
|
|
5832
6123
|
ngOnInit() {
|
|
6124
|
+
this.startLoader();
|
|
5833
6125
|
const settings = this.settings();
|
|
5834
6126
|
if (settings.dyn !== undefined && settings.dyn !== null && settings.dyn.length != 0)
|
|
5835
6127
|
this.dynData.data = settings.dyn;
|
|
5836
6128
|
this.saveState = settings.saveState === undefined ? false : settings.saveState;
|
|
5837
6129
|
this.saveStateId = settings.saveStateId === undefined ? null : settings.saveStateId;
|
|
6130
|
+
this.initCollapsed(settings.collapsed);
|
|
5838
6131
|
this.formFields.set(this.formService.loadState(this.effectiveSaveKey, settings.fields ?? []));
|
|
5839
6132
|
this.extra = settings.extra || null;
|
|
5840
6133
|
this.waiter.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef)).subscribe(event => {
|
|
@@ -5854,6 +6147,26 @@ class XiriQueryComponent {
|
|
|
5854
6147
|
this.cdr.markForCheck();
|
|
5855
6148
|
});
|
|
5856
6149
|
}
|
|
6150
|
+
// The panel only exists when the backend sent `collapsed`. A previously stored state — kept under
|
|
6151
|
+
// the same saveStateId as the filter values — wins over the backend default, so a reload restores
|
|
6152
|
+
// the panel the way the user left it.
|
|
6153
|
+
initCollapsed(fromSettings) {
|
|
6154
|
+
if (fromSettings === undefined)
|
|
6155
|
+
return;
|
|
6156
|
+
const key = this.collapsedSaveKey;
|
|
6157
|
+
const stored = key ? this.storageService.getTimeout(key, 3600) : null;
|
|
6158
|
+
this.collapsed.set(typeof stored === 'boolean' ? stored : fromSettings);
|
|
6159
|
+
}
|
|
6160
|
+
// Material owns the panel; mirror its state back so the next load can restore it. The equality
|
|
6161
|
+
// guard keeps the initial expandedChange emission from rewriting the state nobody touched.
|
|
6162
|
+
panelToggled(expanded) {
|
|
6163
|
+
if (this.collapsed() === !expanded)
|
|
6164
|
+
return;
|
|
6165
|
+
this.collapsed.set(!expanded);
|
|
6166
|
+
const key = this.collapsedSaveKey;
|
|
6167
|
+
if (key)
|
|
6168
|
+
this.storageService.set(key, !expanded);
|
|
6169
|
+
}
|
|
5857
6170
|
formChanged(event) {
|
|
5858
6171
|
this.formValid.set(event.valid);
|
|
5859
6172
|
this.rawValue.set(event.value);
|
|
@@ -5912,52 +6225,64 @@ class XiriQueryComponent {
|
|
|
5912
6225
|
// instead of flashing empty on every navigation/filter change.
|
|
5913
6226
|
this.error.set(null);
|
|
5914
6227
|
this.loading.set(true);
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
else
|
|
5930
|
-
this.data.set([data]);
|
|
5931
|
-
// Pick up an optional result count from the response (backend-driven counts).
|
|
5932
|
-
const counted = res;
|
|
5933
|
-
if (typeof counted.count === 'number')
|
|
5934
|
-
this.responseCount.set({ filtered: counted.count, total: counted.total });
|
|
5935
|
-
this.cdr.markForCheck();
|
|
5936
|
-
},
|
|
5937
|
-
error: (err) => {
|
|
5938
|
-
this.loading.set(false);
|
|
5939
|
-
this.data.set(null);
|
|
5940
|
-
if (err.status === 404)
|
|
5941
|
-
this.error.set('Page not found');
|
|
5942
|
-
else if (err.status === 401)
|
|
5943
|
-
this.error.set('No permission to view page');
|
|
5944
|
-
else if (err.status === 403)
|
|
5945
|
-
this.error.set('Access denied');
|
|
5946
|
-
else if (err.status === 500)
|
|
5947
|
-
this.error.set('Internal Error');
|
|
5948
|
-
else
|
|
5949
|
-
this.error.set('Unknown Error');
|
|
5950
|
-
console.log('XiriQueryComponent error', err);
|
|
5951
|
-
this.cdr.markForCheck();
|
|
5952
|
-
}
|
|
6228
|
+
this.loader.next();
|
|
6229
|
+
}
|
|
6230
|
+
// Alle Loads laufen über einen switchMap: ein noch offener Request wird abgebrochen, sobald
|
|
6231
|
+
// ein neuer startet. Zwei Loads kurz hintereinander sind der Normalfall, sobald ein abhängiges
|
|
6232
|
+
// Feld seine Optionen nachlädt und dabei einen ungültig gewordenen Filterwert verwirft — ohne
|
|
6233
|
+
// switchMap könnte die späte Antwort des überholten Requests die neuere überschreiben.
|
|
6234
|
+
startLoader() {
|
|
6235
|
+
this.loader.pipe(switchMap(() => this.dataService.post(this.settings().url ?? '', this.dynData.filterData)
|
|
6236
|
+
.pipe(catchError((err) => of({ __error: err })))), takeUntilDestroyed(this.destroyRef)).subscribe((res) => {
|
|
6237
|
+
const failed = res?.__error;
|
|
6238
|
+
if (failed)
|
|
6239
|
+
this.loadFailed(failed);
|
|
6240
|
+
else
|
|
6241
|
+
this.loadSucceeded(res);
|
|
5953
6242
|
});
|
|
5954
6243
|
}
|
|
6244
|
+
loadSucceeded(res) {
|
|
6245
|
+
this.loading.set(false);
|
|
6246
|
+
if (!res) {
|
|
6247
|
+
this.data.set(null);
|
|
6248
|
+
this.error.set('Unknown Error');
|
|
6249
|
+
this.cdr.markForCheck();
|
|
6250
|
+
return;
|
|
6251
|
+
}
|
|
6252
|
+
const data = res.data;
|
|
6253
|
+
// check if res is an array
|
|
6254
|
+
if (Array.isArray(data))
|
|
6255
|
+
this.data.set(data);
|
|
6256
|
+
else
|
|
6257
|
+
this.data.set([data]);
|
|
6258
|
+
// Pick up an optional result count from the response (backend-driven counts).
|
|
6259
|
+
const counted = res;
|
|
6260
|
+
if (typeof counted.count === 'number')
|
|
6261
|
+
this.responseCount.set({ filtered: counted.count, total: counted.total });
|
|
6262
|
+
this.cdr.markForCheck();
|
|
6263
|
+
}
|
|
6264
|
+
loadFailed(err) {
|
|
6265
|
+
this.loading.set(false);
|
|
6266
|
+
this.data.set(null);
|
|
6267
|
+
if (err.status === 404)
|
|
6268
|
+
this.error.set('Page not found');
|
|
6269
|
+
else if (err.status === 401)
|
|
6270
|
+
this.error.set('No permission to view page');
|
|
6271
|
+
else if (err.status === 403)
|
|
6272
|
+
this.error.set('Access denied');
|
|
6273
|
+
else if (err.status === 500)
|
|
6274
|
+
this.error.set('Internal Error');
|
|
6275
|
+
else
|
|
6276
|
+
this.error.set('Unknown Error');
|
|
6277
|
+
console.log('XiriQueryComponent error', err);
|
|
6278
|
+
this.cdr.markForCheck();
|
|
6279
|
+
}
|
|
5955
6280
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriQueryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5956
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: XiriQueryComponent, isStandalone: true, selector: "xiri-query", inputs: { settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: true, transformFunction: null }, dyncomponent: { classPropertyName: "dyncomponent", publicName: "dyncomponent", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterChange: "filterChange" }, ngImport: i0, template: "<div class=\"xrow\">\n\t<div class=\"xcol\">\n\t\t@if (
|
|
6281
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: XiriQueryComponent, isStandalone: true, selector: "xiri-query", inputs: { settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: true, transformFunction: null }, dyncomponent: { classPropertyName: "dyncomponent", publicName: "dyncomponent", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterChange: "filterChange" }, ngImport: i0, template: "<div class=\"xrow\">\n\t<div class=\"xcol\">\n\t\t@if (collapsed() !== undefined) {\n\t\t\t<mat-expansion-panel [expanded]=\"!collapsed()\" (expandedChange)=\"panelToggled($event)\" class=\"query-expansion\">\n\t\t\t\t<mat-expansion-panel-header>\n\t\t\t\t\t<mat-panel-title>\n\t\t\t\t\t\t<mat-icon>filter_list</mat-icon> Filter\n\t\t\t\t\t</mat-panel-title>\n\t\t\t\t</mat-expansion-panel-header>\n\t\t\t\t<div class=\"query\">\n\t\t\t\t\t<xiri-form-fields [form]=\"formFields()\" (formChange)=\"formChanged($event)\" display=\"line\"></xiri-form-fields>\n\t\t\t\t\t<ng-container *ngTemplateOutlet=\"filterState\"></ng-container>\n\t\t\t\t\t@if (error()) {\n\t\t\t\t\t\t<div class=\"error\">\n\t\t\t\t\t\t\t<span>{{ error() }}</span>\n\t\t\t\t\t\t\t<button mat-button type=\"button\" data-testid=\"query-retry\" (click)=\"retry()\">\n\t\t\t\t\t\t\t\t<mat-icon>refresh</mat-icon> Retry\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t</mat-expansion-panel>\n\t\t} @else {\n\t\t\t<div class=\"query\">\n\t\t\t\t<xiri-form-fields [form]=\"formFields()\" (formChange)=\"formChanged($event)\" display=\"line\"></xiri-form-fields>\n\t\t\t\t<ng-container *ngTemplateOutlet=\"filterState\"></ng-container>\n\t\t\t\t@if (error()) {\n\t\t\t\t\t<div class=\"error\">\n\t\t\t\t\t\t<span>{{ error() }}</span>\n\t\t\t\t\t\t<button mat-button type=\"button\" data-testid=\"query-retry\" (click)=\"retry()\">\n\t\t\t\t\t\t\t<mat-icon>refresh</mat-icon> Retry\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t</div>\n\t@if (settings().buttonline; as buttonline) {\n\t\t<div class=\"xcol formbtnline\" [class]=\"buttonline.display\">\n\t\t\t<xiri-buttonline [settings]=\"$any(buttonline.data)\" [filterData]=\"filterData()\" [disabled]=\"!formValid()\"\n\t\t\t (result)=\"clickedButton($event)\"></xiri-buttonline>\n\t\t</div>\n\t\t@if (loading()) {\n\t\t\t<div class=\"xcol\">\n\t\t\t\t<mat-spinner diameter=\"30\"></mat-spinner>\n\t\t\t</div>\n\t\t}\n\t}\n\t@if (dynData.data) {\n\t\t<div class=\"xcol\">\n\t\t\t<ng-container *ngTemplateOutlet=\"dyncomponent(); context: { $implicit: dynData }\"></ng-container>\n\t\t</div>\n\t}\n\t@if (data()) {\n\t\t<div class=\"xcol\" [class.is-loading]=\"loading()\">\n\t\t\t<ng-container *ngTemplateOutlet=\"dyncomponent(); context: { $implicit: data() }\"></ng-container>\n\t\t</div>\n\t}\n</div>\n\n<ng-template #filterState>\n\t@if (settings().showActiveFilters && activeFilters().length) {\n\t\t<div class=\"query-filter-state\">\n\t\t\t<mat-chip-set aria-label=\"Aktive Filter\">\n\t\t\t\t@for (filter of activeFilters(); track filter.id) {\n\t\t\t\t\t<mat-chip data-testid=\"query-filter-chip\" (removed)=\"removeFilter(filter.id)\">\n\t\t\t\t\t\t<span class=\"chip-label\">{{ filter.label }}: {{ filter.value }}</span>\n\t\t\t\t\t\t<button matChipRemove type=\"button\"\n\t\t\t\t\t\t [attr.aria-label]=\"'Filter entfernen: ' + filter.label + ' ' + filter.value\">\n\t\t\t\t\t\t\t<mat-icon>cancel</mat-icon>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</mat-chip>\n\t\t\t\t}\n\t\t\t</mat-chip-set>\n\t\t\t<button mat-button type=\"button\" class=\"reset-filters\" data-testid=\"query-reset-filters\"\n\t\t\t (click)=\"resetFilters()\">\n\t\t\t\t<mat-icon>filter_list_off</mat-icon> Alle zur\u00FCcksetzen\n\t\t\t</button>\n\t\t</div>\n\t}\n\t@if (settings().showResultCount && resultCount(); as rc) {\n\t\t<div class=\"query-result-count\" data-testid=\"query-result-count\" aria-live=\"polite\">\n\t\t\t@if (rc.total !== undefined) {\n\t\t\t\t<span><strong>{{ rc.filtered }}</strong> von {{ rc.total }} Treffern</span>\n\t\t\t} @else {\n\t\t\t\t<span><strong>{{ rc.filtered }}</strong> Treffer</span>\n\t\t\t}\n\t\t</div>\n\t}\n</ng-template>\n", styles: [".query{padding-left:var(--xiri-spacing-md);padding-right:var(--xiri-spacing-md)}.is-loading{opacity:.5;transition:opacity .15s ease-in-out;pointer-events:none}.query-filter-state{display:flex;align-items:center;flex-wrap:wrap;gap:var(--xiri-spacing-sm);margin-top:var(--xiri-spacing-sm)}.query-filter-state .reset-filters mat-icon{margin-right:var(--xiri-spacing-xs)}.query-result-count{margin-top:var(--xiri-spacing-sm);color:var(--on-surface-variant, var(--mat-sys-on-surface-variant, inherit));font-size:.9rem}.query-expansion .query{padding-left:0;padding-right:0}.query-expansion mat-panel-title mat-icon{margin-right:var(--xiri-spacing-sm)}\n"], dependencies: [{ kind: "component", type: XiriFormFieldsComponent, selector: "xiri-form-fields", inputs: ["form", "display", "disabled", "check"], outputs: ["formChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: XiriButtonlineComponent, selector: "xiri-buttonline", inputs: ["settings", "disabled", "filterData"], outputs: ["result"] }, { kind: "component", type: MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "component", type: MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "directive", type: MatChipRemove, selector: "[matChipRemove]" }] }); }
|
|
5957
6282
|
}
|
|
5958
6283
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: XiriQueryComponent, decorators: [{
|
|
5959
6284
|
type: Component,
|
|
5960
|
-
args: [{ selector: 'xiri-query', imports: [XiriFormFieldsComponent, NgTemplateOutlet, XiriButtonlineComponent, MatExpansionPanel, MatExpansionPanelHeader, MatExpansionPanelTitle, MatIcon, MatButton, MatProgressSpinner, MatChipSet, MatChip, MatChipRemove], template: "<div class=\"xrow\">\n\t<div class=\"xcol\">\n\t\t@if (
|
|
6285
|
+
args: [{ selector: 'xiri-query', imports: [XiriFormFieldsComponent, NgTemplateOutlet, XiriButtonlineComponent, MatExpansionPanel, MatExpansionPanelHeader, MatExpansionPanelTitle, MatIcon, MatButton, MatProgressSpinner, MatChipSet, MatChip, MatChipRemove], template: "<div class=\"xrow\">\n\t<div class=\"xcol\">\n\t\t@if (collapsed() !== undefined) {\n\t\t\t<mat-expansion-panel [expanded]=\"!collapsed()\" (expandedChange)=\"panelToggled($event)\" class=\"query-expansion\">\n\t\t\t\t<mat-expansion-panel-header>\n\t\t\t\t\t<mat-panel-title>\n\t\t\t\t\t\t<mat-icon>filter_list</mat-icon> Filter\n\t\t\t\t\t</mat-panel-title>\n\t\t\t\t</mat-expansion-panel-header>\n\t\t\t\t<div class=\"query\">\n\t\t\t\t\t<xiri-form-fields [form]=\"formFields()\" (formChange)=\"formChanged($event)\" display=\"line\"></xiri-form-fields>\n\t\t\t\t\t<ng-container *ngTemplateOutlet=\"filterState\"></ng-container>\n\t\t\t\t\t@if (error()) {\n\t\t\t\t\t\t<div class=\"error\">\n\t\t\t\t\t\t\t<span>{{ error() }}</span>\n\t\t\t\t\t\t\t<button mat-button type=\"button\" data-testid=\"query-retry\" (click)=\"retry()\">\n\t\t\t\t\t\t\t\t<mat-icon>refresh</mat-icon> Retry\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t</mat-expansion-panel>\n\t\t} @else {\n\t\t\t<div class=\"query\">\n\t\t\t\t<xiri-form-fields [form]=\"formFields()\" (formChange)=\"formChanged($event)\" display=\"line\"></xiri-form-fields>\n\t\t\t\t<ng-container *ngTemplateOutlet=\"filterState\"></ng-container>\n\t\t\t\t@if (error()) {\n\t\t\t\t\t<div class=\"error\">\n\t\t\t\t\t\t<span>{{ error() }}</span>\n\t\t\t\t\t\t<button mat-button type=\"button\" data-testid=\"query-retry\" (click)=\"retry()\">\n\t\t\t\t\t\t\t<mat-icon>refresh</mat-icon> Retry\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\t</div>\n\t@if (settings().buttonline; as buttonline) {\n\t\t<div class=\"xcol formbtnline\" [class]=\"buttonline.display\">\n\t\t\t<xiri-buttonline [settings]=\"$any(buttonline.data)\" [filterData]=\"filterData()\" [disabled]=\"!formValid()\"\n\t\t\t (result)=\"clickedButton($event)\"></xiri-buttonline>\n\t\t</div>\n\t\t@if (loading()) {\n\t\t\t<div class=\"xcol\">\n\t\t\t\t<mat-spinner diameter=\"30\"></mat-spinner>\n\t\t\t</div>\n\t\t}\n\t}\n\t@if (dynData.data) {\n\t\t<div class=\"xcol\">\n\t\t\t<ng-container *ngTemplateOutlet=\"dyncomponent(); context: { $implicit: dynData }\"></ng-container>\n\t\t</div>\n\t}\n\t@if (data()) {\n\t\t<div class=\"xcol\" [class.is-loading]=\"loading()\">\n\t\t\t<ng-container *ngTemplateOutlet=\"dyncomponent(); context: { $implicit: data() }\"></ng-container>\n\t\t</div>\n\t}\n</div>\n\n<ng-template #filterState>\n\t@if (settings().showActiveFilters && activeFilters().length) {\n\t\t<div class=\"query-filter-state\">\n\t\t\t<mat-chip-set aria-label=\"Aktive Filter\">\n\t\t\t\t@for (filter of activeFilters(); track filter.id) {\n\t\t\t\t\t<mat-chip data-testid=\"query-filter-chip\" (removed)=\"removeFilter(filter.id)\">\n\t\t\t\t\t\t<span class=\"chip-label\">{{ filter.label }}: {{ filter.value }}</span>\n\t\t\t\t\t\t<button matChipRemove type=\"button\"\n\t\t\t\t\t\t [attr.aria-label]=\"'Filter entfernen: ' + filter.label + ' ' + filter.value\">\n\t\t\t\t\t\t\t<mat-icon>cancel</mat-icon>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</mat-chip>\n\t\t\t\t}\n\t\t\t</mat-chip-set>\n\t\t\t<button mat-button type=\"button\" class=\"reset-filters\" data-testid=\"query-reset-filters\"\n\t\t\t (click)=\"resetFilters()\">\n\t\t\t\t<mat-icon>filter_list_off</mat-icon> Alle zur\u00FCcksetzen\n\t\t\t</button>\n\t\t</div>\n\t}\n\t@if (settings().showResultCount && resultCount(); as rc) {\n\t\t<div class=\"query-result-count\" data-testid=\"query-result-count\" aria-live=\"polite\">\n\t\t\t@if (rc.total !== undefined) {\n\t\t\t\t<span><strong>{{ rc.filtered }}</strong> von {{ rc.total }} Treffern</span>\n\t\t\t} @else {\n\t\t\t\t<span><strong>{{ rc.filtered }}</strong> Treffer</span>\n\t\t\t}\n\t\t</div>\n\t}\n</ng-template>\n", styles: [".query{padding-left:var(--xiri-spacing-md);padding-right:var(--xiri-spacing-md)}.is-loading{opacity:.5;transition:opacity .15s ease-in-out;pointer-events:none}.query-filter-state{display:flex;align-items:center;flex-wrap:wrap;gap:var(--xiri-spacing-sm);margin-top:var(--xiri-spacing-sm)}.query-filter-state .reset-filters mat-icon{margin-right:var(--xiri-spacing-xs)}.query-result-count{margin-top:var(--xiri-spacing-sm);color:var(--on-surface-variant, var(--mat-sys-on-surface-variant, inherit));font-size:.9rem}.query-expansion .query{padding-left:0;padding-right:0}.query-expansion mat-panel-title mat-icon{margin-right:var(--xiri-spacing-sm)}\n"] }]
|
|
5961
6286
|
}], propDecorators: { settings: [{ type: i0.Input, args: [{ isSignal: true, alias: "settings", required: true }] }], dyncomponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "dyncomponent", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }] } });
|
|
5962
6287
|
// A filter counts as active unless it is null/undefined, an empty string, an empty array,
|
|
5963
6288
|
// a boolean false, or a range object with no bounds set.
|
|
@@ -5987,15 +6312,6 @@ function formatFilterValue(field, value) {
|
|
|
5987
6312
|
return field.name ?? 'Ja';
|
|
5988
6313
|
return String(value);
|
|
5989
6314
|
}
|
|
5990
|
-
// The "empty" value used to clear a control, matching the shape the control currently holds.
|
|
5991
|
-
function emptyValueForField(field) {
|
|
5992
|
-
const value = field.control?.value;
|
|
5993
|
-
if (Array.isArray(value))
|
|
5994
|
-
return [];
|
|
5995
|
-
if (typeof value === 'string')
|
|
5996
|
-
return '';
|
|
5997
|
-
return null;
|
|
5998
|
-
}
|
|
5999
6315
|
|
|
6000
6316
|
class XiriLinksComponent {
|
|
6001
6317
|
constructor() {
|