@grumptech/ngx-formly-editor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1714 @@
1
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
2
+ import { MatSnackBar } from '@angular/material/snack-bar';
3
+ import { BehaviorSubject, Subject, fromEvent, debounceTime, map, startWith, skip } from 'rxjs';
4
+ import * as i0 from '@angular/core';
5
+ import { Injectable, InjectionToken, inject, signal, ElementRef, ChangeDetectorRef, DestroyRef, ChangeDetectionStrategy, Component, model, viewChild, output, input } from '@angular/core';
6
+ import { toFlatArray, jsonParse, jsonStringify, cleanInput, jsonParseFieldsAndValidate } from '@grumptech/formly-converters';
7
+ import { FormlyConfig, FieldArrayType, FormlyForm } from '@ngx-formly/core';
8
+ import { MatxPanel, MatxPanelHeaderButton } from '@grumptech/ngx-matx/panel';
9
+ import { CodeEditor } from '@grumptech/ngx-basic-ui/code-editor';
10
+ import * as i1 from '@angular/forms';
11
+ import { FormGroup, ReactiveFormsModule, UntypedFormGroup, FormControl } from '@angular/forms';
12
+ import { AsyncPipe } from '@angular/common';
13
+ import { MatIcon } from '@angular/material/icon';
14
+ import { MatxDragDropTree } from '@grumptech/ngx-matx/drag-drop-tree';
15
+ import { MatButton } from '@angular/material/button';
16
+ import { MatxFullWidthFormFields } from '@grumptech/ngx-matx/full-width-form-fields';
17
+ import JSON5 from 'json5';
18
+ import * as i2 from '@angular/material/autocomplete';
19
+ import { MatAutocompleteModule } from '@angular/material/autocomplete';
20
+ import { MatInput } from '@angular/material/input';
21
+ import * as i3 from '@angular/material/form-field';
22
+ import { MatFormFieldModule } from '@angular/material/form-field';
23
+
24
+ class TestValueManager {
25
+ changeSubject = new BehaviorSubject(null);
26
+ onChange = this.changeSubject.asObservable();
27
+ ngOnDestroy() {
28
+ this.changeSubject.complete();
29
+ }
30
+ set(value) {
31
+ this.changeSubject.next(value);
32
+ }
33
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TestValueManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
34
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TestValueManager });
35
+ }
36
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TestValueManager, decorators: [{
37
+ type: Injectable
38
+ }] });
39
+
40
+ class UndoManager {
41
+ states = [];
42
+ currentState = 0;
43
+ change = new Subject();
44
+ onChange = this.change.asObservable();
45
+ ngOnDestroy() {
46
+ this.change.complete();
47
+ }
48
+ push(state) {
49
+ if (this.currentState < this.states.length - 1) {
50
+ this.states.splice(this.currentState + 1);
51
+ }
52
+ this.states.push(JSON.stringify(state));
53
+ this.currentState = this.states.length - 1;
54
+ }
55
+ clear() {
56
+ this.states.splice(0, this.states.length - 1);
57
+ this.currentState = 0;
58
+ }
59
+ undo() {
60
+ if (this.currentState > 0) {
61
+ this.currentState--;
62
+ this.change.next(JSON.parse(this.states[this.currentState]));
63
+ }
64
+ }
65
+ redo() {
66
+ if (this.currentState < this.states.length - 1) {
67
+ this.currentState++;
68
+ this.change.next(JSON.parse(this.states[this.currentState]));
69
+ }
70
+ }
71
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: UndoManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
72
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: UndoManager });
73
+ }
74
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: UndoManager, decorators: [{
75
+ type: Injectable
76
+ }] });
77
+
78
+ class FieldConfigBuilder {
79
+ fields = [];
80
+ parentByField = new Map();
81
+ build() {
82
+ return this.fields;
83
+ }
84
+ set(fields) {
85
+ this.fields = fields;
86
+ this.fields.forEach((i) => this.addToMapsRecursive(i));
87
+ return this;
88
+ }
89
+ add(field, idx, parent) {
90
+ let list = this.fields;
91
+ if (parent) {
92
+ parent.fieldGroup = parent.fieldGroup || [];
93
+ list = parent.fieldGroup;
94
+ }
95
+ list.splice(idx, 0, field);
96
+ this.addToMapsRecursive(field, parent);
97
+ return this;
98
+ }
99
+ addToFieldArray(field, parent, first = true) {
100
+ if (typeof parent.fieldArray === 'object') {
101
+ field = {
102
+ type: 'formly-group',
103
+ fieldGroup: first
104
+ ? [field, parent.fieldArray]
105
+ : [parent.fieldArray, field],
106
+ };
107
+ this.removeFromMapsRecursive(parent.fieldArray);
108
+ }
109
+ parent.fieldArray = field;
110
+ this.addToMapsRecursive(field, parent);
111
+ return this;
112
+ }
113
+ insertAfter(field, referenceField) {
114
+ const parent = this.parentByField.get(referenceField);
115
+ if (parent?.fieldArray === referenceField) {
116
+ return this.addToFieldArray(field, parent, false);
117
+ }
118
+ const index = this.getIndex(referenceField, parent?.fieldGroup || this.fields);
119
+ return this.add(field, index + 1, parent);
120
+ }
121
+ update(field, newField) {
122
+ const parent = this.parentByField.get(field);
123
+ if (parent && parent.fieldArray === field) {
124
+ parent.fieldArray = newField;
125
+ }
126
+ else {
127
+ const list = parent ? parent.fieldGroup || [] : this.fields;
128
+ const idx = this.getIndex(field, list);
129
+ list.splice(idx, 1, newField);
130
+ }
131
+ this.removeFromMapsRecursive(field);
132
+ this.addToMapsRecursive(newField);
133
+ return this;
134
+ }
135
+ remove(field) {
136
+ const parent = this.parentByField.get(field);
137
+ if (parent && parent.fieldArray === field) {
138
+ delete parent.fieldArray;
139
+ }
140
+ else {
141
+ const list = parent ? parent.fieldGroup || [] : this.fields;
142
+ const idx = this.getIndex(field, list);
143
+ list.splice(idx, 1);
144
+ }
145
+ this.removeFromMapsRecursive(field);
146
+ return this;
147
+ }
148
+ getIndex(field, list) {
149
+ const idx = list.indexOf(field);
150
+ if (idx === -1) {
151
+ throw new Error(`Formly builder: field not found: ${JSON.stringify(field)}`);
152
+ }
153
+ return idx;
154
+ }
155
+ addToMapsRecursive(field, parent) {
156
+ if (parent) {
157
+ this.parentByField.set(field, parent);
158
+ }
159
+ if (typeof field.fieldArray === 'object') {
160
+ this.addToMapsRecursive(field.fieldArray, field);
161
+ }
162
+ field.fieldGroup?.forEach((i) => this.addToMapsRecursive(i, field));
163
+ }
164
+ removeFromMapsRecursive(field) {
165
+ this.parentByField.delete(field);
166
+ if (typeof field.fieldArray === 'object') {
167
+ this.removeFromMapsRecursive(field.fieldArray);
168
+ }
169
+ field.fieldGroup?.forEach((i) => this.removeFromMapsRecursive(i));
170
+ }
171
+ }
172
+
173
+ const defaultProperties$1 = {
174
+ key: {
175
+ key: 'key',
176
+ type: 'formly-editor-input',
177
+ props: { label: 'key' },
178
+ },
179
+ id: {
180
+ key: 'id',
181
+ type: 'formly-editor-input',
182
+ props: { label: 'id' },
183
+ },
184
+ name: {
185
+ key: 'name',
186
+ type: 'formly-editor-input',
187
+ props: { label: 'name' },
188
+ },
189
+ type: {
190
+ key: 'type',
191
+ type: 'formly-editor-select',
192
+ props: { label: 'type' },
193
+ },
194
+ className: {
195
+ key: 'className',
196
+ type: 'formly-editor-input',
197
+ props: { label: 'className' },
198
+ },
199
+ template: {
200
+ key: 'template',
201
+ type: 'formly-editor-textarea',
202
+ props: { label: 'template' },
203
+ },
204
+ defaultValue: {
205
+ key: 'defaultValue',
206
+ type: 'formly-editor-input',
207
+ props: { label: 'defaultValue' },
208
+ },
209
+ hide: {
210
+ key: 'hide',
211
+ type: 'formly-editor-checkbox',
212
+ props: { label: 'hide' },
213
+ },
214
+ focus: {
215
+ key: 'focus',
216
+ type: 'formly-editor-checkbox',
217
+ props: { label: 'focus' },
218
+ },
219
+ fieldGroupClassName: {
220
+ key: 'fieldGroupClassName',
221
+ type: 'formly-editor-input',
222
+ props: { label: 'fieldGroupClassName' },
223
+ },
224
+ 'props.type': {
225
+ key: 'props.type',
226
+ type: 'formly-editor-input',
227
+ props: { label: 'type' },
228
+ },
229
+ 'props.label': {
230
+ key: 'props.label',
231
+ type: 'formly-editor-input',
232
+ props: { label: 'label' },
233
+ },
234
+ 'props.placeholder': {
235
+ key: 'props.placeholder',
236
+ type: 'formly-editor-input',
237
+ props: { label: 'placeholder' },
238
+ },
239
+ 'props.disabled': {
240
+ key: 'props.disabled',
241
+ type: 'formly-editor-checkbox',
242
+ props: { label: 'disabled' },
243
+ },
244
+ 'props.options': {
245
+ key: 'props.options',
246
+ type: 'formly-editor-array-dialog',
247
+ props: {
248
+ label: 'options',
249
+ dialogTitle: 'Options',
250
+ field: {
251
+ fieldGroup: [
252
+ {
253
+ key: 'label',
254
+ type: 'formly-editor-input',
255
+ props: { label: 'Label' },
256
+ },
257
+ {
258
+ key: 'value',
259
+ type: 'formly-editor-input',
260
+ props: { label: 'Value' },
261
+ },
262
+ ],
263
+ },
264
+ },
265
+ },
266
+ 'props.rows': {
267
+ key: 'props.rows',
268
+ type: 'formly-editor-input-number',
269
+ props: { label: 'rows' },
270
+ },
271
+ 'props.cols': {
272
+ key: 'props.cols',
273
+ type: 'formly-editor-input-number',
274
+ props: { label: 'cols' },
275
+ },
276
+ 'props.description': {
277
+ key: 'props.description',
278
+ type: 'formly-editor-input',
279
+ props: { label: 'description' },
280
+ },
281
+ 'props.hidden': {
282
+ key: 'props.hidden',
283
+ type: 'formly-editor-checkbox',
284
+ props: { label: 'hidden' },
285
+ },
286
+ 'props.max': {
287
+ key: 'props.max',
288
+ type: 'formly-editor-input-number',
289
+ props: { label: 'max' },
290
+ },
291
+ 'props.min': {
292
+ key: 'props.min',
293
+ type: 'formly-editor-input-number',
294
+ props: { label: 'min' },
295
+ },
296
+ 'props.minLength': {
297
+ key: 'props.minLength',
298
+ type: 'formly-editor-input-number',
299
+ props: { label: 'minLength' },
300
+ },
301
+ 'props.maxLength': {
302
+ key: 'props.maxLength',
303
+ type: 'formly-editor-input-number',
304
+ props: { label: 'maxLength' },
305
+ },
306
+ 'props.pattern': {
307
+ key: 'props.pattern',
308
+ type: 'formly-editor-input',
309
+ props: { label: 'pattern' },
310
+ },
311
+ 'props.required': {
312
+ key: 'props.required',
313
+ type: 'formly-editor-checkbox',
314
+ props: { label: 'required' },
315
+ },
316
+ 'props.tabindex': {
317
+ key: 'props.tabindex',
318
+ type: 'formly-editor-input-number',
319
+ props: { label: 'tabindex' },
320
+ },
321
+ 'props.readonly': {
322
+ key: 'props.readonly',
323
+ type: 'formly-editor-checkbox',
324
+ props: { label: 'readonly' },
325
+ },
326
+ 'props.step': {
327
+ key: 'props.step',
328
+ type: 'formly-editor-input-number',
329
+ props: { label: 'step' },
330
+ },
331
+ spacing: {},
332
+ 'props.url': {
333
+ key: 'props.url',
334
+ type: 'formly-editor-input',
335
+ props: { label: 'url' },
336
+ },
337
+ 'props.method': {
338
+ key: 'props.method',
339
+ type: 'formly-editor-input',
340
+ props: { label: 'method' },
341
+ },
342
+ 'props.valueKey': {
343
+ key: 'props.valueKey',
344
+ type: 'formly-editor-input',
345
+ props: { label: 'value key' },
346
+ },
347
+ 'props.labelKey': {
348
+ key: 'props.labelKey',
349
+ type: 'formly-editor-input',
350
+ props: { label: 'label key' },
351
+ },
352
+ 'props.inputGroups': {
353
+ key: 'props.inputGroups',
354
+ type: 'formly-editor-select',
355
+ props: {
356
+ label: 'input groups',
357
+ multiple: true,
358
+ options: [
359
+ { label: 'path', value: 'path' },
360
+ { label: 'query', value: 'query' },
361
+ { label: 'body', value: 'body' },
362
+ { label: 'result', value: 'result' },
363
+ ],
364
+ },
365
+ },
366
+ 'props.responseType': {
367
+ key: 'props.responseType',
368
+ type: 'formly-editor-select',
369
+ props: {
370
+ label: 'response type',
371
+ options: [
372
+ { label: 'none', value: undefined },
373
+ { label: 'arraybuffer', value: 'arraybuffer' },
374
+ { label: 'blob', value: 'blob' },
375
+ { label: 'json', value: 'json' },
376
+ { label: 'text', value: 'text' },
377
+ ],
378
+ },
379
+ },
380
+ 'props.addButtonLabel': {
381
+ key: 'props.addButtonLabel',
382
+ type: 'formly-editor-input',
383
+ props: { label: 'add button label' },
384
+ },
385
+ 'props.emptyArrayMessage': {
386
+ key: 'props.emptyArrayMessage',
387
+ type: 'formly-editor-input',
388
+ props: { label: 'empty array message' },
389
+ },
390
+ 'props.navigationDisabled': {
391
+ key: 'props.navigationDisabled',
392
+ type: 'formly-editor-checkbox',
393
+ props: { label: 'disable navigation' },
394
+ },
395
+ 'props.action': {
396
+ key: 'props.action',
397
+ type: 'formly-editor-select',
398
+ props: {
399
+ label: 'action',
400
+ options: [
401
+ { label: 'send-and-clear', value: 'send-and-clear' },
402
+ { label: 'send-and-close', value: 'send-and-close' },
403
+ { label: 'send-and-navigate', value: 'send-and-navigate' },
404
+ { label: 'send-and-show-message', value: 'send-and-show-message' },
405
+ ],
406
+ },
407
+ },
408
+ 'props.autoRun': {
409
+ key: 'props.autoRun',
410
+ type: 'formly-editor-checkbox',
411
+ props: { label: 'run automatically' },
412
+ },
413
+ 'props.form': {
414
+ key: 'props.form',
415
+ type: 'formly-editor-input',
416
+ props: { label: 'form' },
417
+ },
418
+ 'props.menu': {
419
+ key: 'props.menu',
420
+ type: 'formly-editor-menu-dialog',
421
+ props: { label: 'menu' },
422
+ },
423
+ 'props.pages': {
424
+ key: 'props.pages',
425
+ type: 'formly-editor-array-dialog',
426
+ props: {
427
+ label: 'pages',
428
+ dialogTitle: 'Pages',
429
+ width: 800,
430
+ field: {
431
+ fieldGroup: [
432
+ {
433
+ key: 'url',
434
+ type: 'formly-editor-input',
435
+ props: { label: 'url' },
436
+ },
437
+ {
438
+ key: 'breadcrumbParts',
439
+ type: 'formly-editor-array-dialog',
440
+ props: {
441
+ label: 'breadcrumb parts',
442
+ dialogTitle: 'Breadcrumb parts',
443
+ width: 750,
444
+ field: {
445
+ fieldGroup: [
446
+ {
447
+ key: 'label',
448
+ type: 'formly-editor-input',
449
+ props: { label: 'label' },
450
+ },
451
+ {
452
+ key: 'url',
453
+ type: 'formly-editor-input',
454
+ props: { label: 'url' },
455
+ },
456
+ ],
457
+ },
458
+ },
459
+ },
460
+ ],
461
+ },
462
+ },
463
+ },
464
+ };
465
+
466
+ const defaultProperties = [
467
+ 'key',
468
+ 'type',
469
+ 'props.required',
470
+ 'props.label',
471
+ 'props.placeholder',
472
+ 'props.description',
473
+ 'hide',
474
+ 'props.disabled',
475
+ 'props.readonly',
476
+ ];
477
+ const propertiesWithOptions = [
478
+ 'key',
479
+ 'type',
480
+ 'props.required',
481
+ 'props.label',
482
+ 'props.placeholder',
483
+ 'props.description',
484
+ 'hide',
485
+ 'props.disabled',
486
+ 'props.readonly',
487
+ 'props.options',
488
+ ];
489
+ const propertiesArrayType = [
490
+ 'key',
491
+ 'type',
492
+ 'props.required',
493
+ 'props.label',
494
+ 'hide',
495
+ 'props.disabled',
496
+ 'props.readonly',
497
+ 'props.emptyArrayMessage',
498
+ 'props.addButtonLabel',
499
+ ];
500
+ const propertiesActionType = [
501
+ 'key',
502
+ 'type',
503
+ 'props.inputGroups',
504
+ 'props.label',
505
+ 'props.url',
506
+ 'props.method',
507
+ 'props.responseType',
508
+ 'props.action',
509
+ 'hide',
510
+ 'props.disabled',
511
+ ];
512
+ const defaultPropertiesByType = {
513
+ action: propertiesActionType,
514
+ app: ['spacing', 'type', 'props.menu', 'props.pages'],
515
+ 'app-breadcrumb': ['key', 'type'],
516
+ 'app-navigation': ['key', 'type'],
517
+ array: propertiesArrayType,
518
+ boolean: defaultProperties,
519
+ checkbox: defaultProperties,
520
+ datepicker: defaultProperties,
521
+ datetimepicker: defaultProperties,
522
+ 'dialog-button': ['key', 'type', 'props.label', 'props.form', 'hide'],
523
+ enum: propertiesWithOptions,
524
+ 'error-message': defaultProperties,
525
+ 'formly-template': ['spacing', 'template'],
526
+ grid: propertiesArrayType,
527
+ input: defaultProperties,
528
+ integer: defaultProperties,
529
+ link: ['key', 'type', 'props.label', 'props.url', 'hide'],
530
+ 'load-action': propertiesActionType
531
+ .map((i) => (i === 'props.action' ? 'spacing' : i))
532
+ .concat(['props.autoRun']),
533
+ 'formly-group': ['key', 'type', 'hide', 'props.disabled'],
534
+ message: defaultProperties,
535
+ 'message-container': ['spacing', 'type'],
536
+ number: defaultProperties,
537
+ multicheckbox: propertiesWithOptions,
538
+ object: [
539
+ 'key',
540
+ 'type',
541
+ 'props.required',
542
+ 'props.label',
543
+ 'hide',
544
+ 'props.disabled',
545
+ 'props.readonly',
546
+ ],
547
+ page: ['props.navigationDisabled', 'type'],
548
+ 'save-action': propertiesActionType.map((i) => i === 'props.action' ? 'spacing' : i),
549
+ radio: propertiesWithOptions,
550
+ select: propertiesWithOptions,
551
+ string: defaultProperties,
552
+ textarea: defaultProperties.slice().concat(['props.rows']),
553
+ 'url-select': [
554
+ 'key',
555
+ 'type',
556
+ 'props.required',
557
+ 'props.label',
558
+ 'props.placeholder',
559
+ 'props.description',
560
+ 'hide',
561
+ 'props.disabled',
562
+ 'props.readonly',
563
+ 'props.url',
564
+ 'props.valueKey',
565
+ 'props.labelKey',
566
+ ],
567
+ };
568
+
569
+ const defaultEditorConfig = {
570
+ groupTypes: ['formly-group', 'object', 'app', 'page', 'message-container'],
571
+ properties: defaultProperties$1,
572
+ propertiesByType: defaultPropertiesByType,
573
+ formRenderConfig: {
574
+ editFormFieldConverter: (field) => {
575
+ field.hide && delete field.hide;
576
+ field.expressions && delete field.expressions;
577
+ },
578
+ testFormFieldConverter: () => { },
579
+ },
580
+ };
581
+
582
+ const FORMLY_EDITOR_CONFIG = new InjectionToken('formlyEditorConfig');
583
+
584
+ class EditorConfigReader {
585
+ groupTypes;
586
+ properties;
587
+ propertiesByType;
588
+ formRenderConfig;
589
+ constructor() {
590
+ const config = inject(FORMLY_EDITOR_CONFIG);
591
+ const defaults = defaultEditorConfig;
592
+ this.groupTypes = config.groupTypes ?? defaults.groupTypes ?? [];
593
+ this.properties = Object.assign({}, defaults.properties, config.properties);
594
+ this.propertiesByType = Object.assign({}, defaults.propertiesByType, config.propertiesByType);
595
+ this.formRenderConfig = Object.assign({}, defaults.formRenderConfig, config.formRenderConfig);
596
+ }
597
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: EditorConfigReader, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
598
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: EditorConfigReader });
599
+ }
600
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: EditorConfigReader, decorators: [{
601
+ type: Injectable
602
+ }], ctorParameters: () => [] });
603
+
604
+ class FieldTypesReader {
605
+ formlyConfig = inject(FormlyConfig);
606
+ groupTypes = inject(EditorConfigReader).groupTypes;
607
+ initialized = false;
608
+ fieldTypes = [];
609
+ fieldTypesSet = new Set();
610
+ fieldArrayTypesSet = new Set();
611
+ fieldGroupTypesSet = new Set();
612
+ initialize() {
613
+ if (this.initialized) {
614
+ return;
615
+ }
616
+ const fieldTypes = this.formlyConfig.types;
617
+ this.fieldTypes = Object.freeze(Object.keys(fieldTypes)
618
+ .filter((i) => !i.startsWith('formly-editor-'))
619
+ .sort((x, y) => x.localeCompare(y)));
620
+ this.fieldTypesSet = new Set(this.fieldTypes);
621
+ this.fieldGroupTypesSet = new Set(this.groupTypes);
622
+ this.fieldArrayTypesSet = new Set(this.fieldTypes.filter((i) => fieldTypes[i].component?.prototype instanceof FieldArrayType));
623
+ this.initialized = true;
624
+ }
625
+ get() {
626
+ this.initialize();
627
+ return this.fieldTypes;
628
+ }
629
+ getType(field) {
630
+ this.initialize();
631
+ if (typeof field.type === 'string') {
632
+ return field.type;
633
+ }
634
+ return field.template ? 'formly-template' : 'formly-group';
635
+ }
636
+ getStandardTypes() {
637
+ this.initialize();
638
+ return this.fieldTypes.filter((i) => i !== 'formly-template' &&
639
+ !this.fieldGroupTypesSet.has(i) &&
640
+ !this.fieldArrayTypesSet.has(i));
641
+ }
642
+ getGroupTypes() {
643
+ this.initialize();
644
+ return this.fieldTypes.filter((i) => this.fieldGroupTypesSet.has(i));
645
+ }
646
+ getArrayTypes() {
647
+ this.initialize();
648
+ return this.fieldTypes.filter((i) => this.fieldArrayTypesSet.has(i));
649
+ }
650
+ hasExistingType(field) {
651
+ this.initialize();
652
+ return (!('type' in field) ||
653
+ (typeof field.type === 'string' && this.fieldTypesSet.has(field.type)));
654
+ }
655
+ isFieldGroup(field) {
656
+ this.initialize();
657
+ return this.fieldGroupTypesSet.has(this.getType(field));
658
+ }
659
+ isFieldArray(field) {
660
+ this.initialize();
661
+ return (typeof field.type === 'string' && this.fieldArrayTypesSet.has(field.type));
662
+ }
663
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FieldTypesReader, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
664
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FieldTypesReader });
665
+ }
666
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FieldTypesReader, decorators: [{
667
+ type: Injectable
668
+ }] });
669
+
670
+ class SelectionManager {
671
+ selection = new Set();
672
+ selectSubject = new BehaviorSubject(null);
673
+ onSelect = this.selectSubject.asObservable();
674
+ ngOnDestroy() {
675
+ this.selectSubject.complete();
676
+ }
677
+ has(key) {
678
+ return this.selection.has(key);
679
+ }
680
+ get() {
681
+ return [...this.selection.keys()];
682
+ }
683
+ getLast() {
684
+ return this.selectSubject.value;
685
+ }
686
+ set(selection) {
687
+ this.selection = new Set(selection);
688
+ const last = this.selectSubject.value;
689
+ this.next(last && !this.selection.has(last) ? null : last);
690
+ }
691
+ replace(oldKey, newKey) {
692
+ if (this.selection.has(oldKey)) {
693
+ this.selection.delete(oldKey);
694
+ this.selection.add(newKey);
695
+ this.next(newKey);
696
+ }
697
+ }
698
+ clear() {
699
+ if (this.selection.size !== 0) {
700
+ this.selection.clear();
701
+ }
702
+ this.next(null);
703
+ }
704
+ select(key, ctrlKey, shiftKey) {
705
+ if (key === null) {
706
+ return this.clear();
707
+ }
708
+ if (ctrlKey || shiftKey) {
709
+ return this.toggle(key);
710
+ }
711
+ if (this.selection.has(key) && this.selection.size === 1) {
712
+ return this.next(key);
713
+ }
714
+ this.selection.clear();
715
+ this.selection.add(key);
716
+ this.next(key);
717
+ }
718
+ toggle(key) {
719
+ if (this.selection.has(key)) {
720
+ this.selection.delete(key);
721
+ this.next(null);
722
+ }
723
+ else {
724
+ this.selection.add(key);
725
+ this.next(key);
726
+ }
727
+ }
728
+ next(key) {
729
+ this.selectSubject.next(key);
730
+ }
731
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SelectionManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
732
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SelectionManager });
733
+ }
734
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SelectionManager, decorators: [{
735
+ type: Injectable
736
+ }] });
737
+
738
+ class DataManager {
739
+ onChange;
740
+ selection = inject((SelectionManager));
741
+ undoManager = inject((UndoManager));
742
+ formlyFieldTypes = inject(FieldTypesReader);
743
+ fields = [];
744
+ change = new BehaviorSubject([]);
745
+ constructor() {
746
+ this.onChange = this.change.asObservable();
747
+ this.undoManager.onChange.subscribe((state) => {
748
+ this.selection.clear();
749
+ this.fields.splice(0);
750
+ this.fields.push(...state);
751
+ this.change.next(this.fields);
752
+ });
753
+ }
754
+ ngOnDestroy() {
755
+ this.change.complete();
756
+ }
757
+ setFields(fields) {
758
+ this.undoManager.clear();
759
+ this.selection.clear();
760
+ this.fields = fields;
761
+ this.update();
762
+ }
763
+ getFields() {
764
+ return this.fields;
765
+ }
766
+ addFields(fields) {
767
+ if (fields.length === 0) {
768
+ return;
769
+ }
770
+ const selectedField = this.selection.getLast();
771
+ const configBuilder = new FieldConfigBuilder().set(this.fields);
772
+ if (!selectedField) {
773
+ fields.forEach((i) => configBuilder.add(i, this.fields.length));
774
+ }
775
+ else if (this.formlyFieldTypes.isFieldGroup(selectedField)) {
776
+ fields.forEach((i, idx) => configBuilder.add(i, idx, selectedField ?? undefined));
777
+ }
778
+ else if (!this.formlyFieldTypes.isFieldArray(selectedField)) {
779
+ fields
780
+ .reverse()
781
+ .forEach((i) => configBuilder.insertAfter(i, selectedField));
782
+ }
783
+ else if (typeof selectedField.fieldArray === 'object') {
784
+ configBuilder.update(selectedField.fieldArray, {
785
+ type: 'formly-group',
786
+ fieldGroup: [...fields, selectedField.fieldArray],
787
+ });
788
+ }
789
+ else {
790
+ configBuilder.addToFieldArray(fields.length > 1
791
+ ? { type: 'formly-group', fieldGroup: fields }
792
+ : fields[0], selectedField);
793
+ }
794
+ this.update();
795
+ }
796
+ updateField(oldField, newField) {
797
+ const configBuilder = new FieldConfigBuilder().set(this.fields);
798
+ configBuilder.update(oldField, newField);
799
+ this.selection.replace(oldField, newField);
800
+ this.update();
801
+ }
802
+ moveField(field, targetIndex, target) {
803
+ const configBuilder = new FieldConfigBuilder().set(this.fields);
804
+ configBuilder.remove(field);
805
+ if (!target || !this.formlyFieldTypes.isFieldArray(target)) {
806
+ configBuilder.add(field, targetIndex, target);
807
+ }
808
+ else {
809
+ configBuilder.addToFieldArray(field, target, !targetIndex);
810
+ }
811
+ this.update();
812
+ }
813
+ removeSelectedFields() {
814
+ const fields = this.getSelectedFields();
815
+ if (fields.length) {
816
+ const configBuilder = new FieldConfigBuilder().set(this.fields);
817
+ fields.forEach((i) => configBuilder.remove(i));
818
+ this.selection.clear();
819
+ this.update();
820
+ }
821
+ }
822
+ getSelectedFields() {
823
+ return this.getSelectedFieldsRecursive(this.fields);
824
+ }
825
+ getSelectedFieldsRecursive(fields) {
826
+ return fields.flatMap((i) => {
827
+ if (this.selection.has(i)) {
828
+ return i;
829
+ }
830
+ const children = (typeof i.fieldArray === 'object' ? [i.fieldArray] : []).concat(i.fieldGroup || []);
831
+ return this.getSelectedFieldsRecursive(children);
832
+ });
833
+ }
834
+ update() {
835
+ this.undoManager.push(this.fields);
836
+ this.change.next(this.fields);
837
+ }
838
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DataManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
839
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DataManager });
840
+ }
841
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DataManager, decorators: [{
842
+ type: Injectable
843
+ }], ctorParameters: () => [] });
844
+
845
+ class FieldMarker {
846
+ FORMLY_DESIGNER_CLASS_PREFIX = 'formly-designer-class-prefix_';
847
+ markField(field, id) {
848
+ field.className =
849
+ (field.className ? `${field.className} ` : '') +
850
+ `${this.FORMLY_DESIGNER_CLASS_PREFIX}${id}`;
851
+ }
852
+ getIdFromElement(element) {
853
+ for (let i = 0, l = element.classList.length; i < l; i++) {
854
+ const cssClass = element.classList[i];
855
+ if (cssClass.startsWith(this.FORMLY_DESIGNER_CLASS_PREFIX)) {
856
+ return cssClass.replace(this.FORMLY_DESIGNER_CLASS_PREFIX, '');
857
+ }
858
+ }
859
+ return null;
860
+ }
861
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FieldMarker, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
862
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FieldMarker, providedIn: 'root' });
863
+ }
864
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FieldMarker, decorators: [{
865
+ type: Injectable,
866
+ args: [{ providedIn: 'root' }]
867
+ }] });
868
+
869
+ class DataValidator {
870
+ fieldTypesReader = inject(FieldTypesReader);
871
+ validate(fields) {
872
+ const result = new Map();
873
+ if (fields.length > 1) {
874
+ this.validateField({ fieldGroup: fields }, result);
875
+ }
876
+ toFlatArray(fields).forEach((i, idx) => {
877
+ if (idx !== 0 ||
878
+ fields.length > 1 ||
879
+ !this.fieldTypesReader.isFieldArray(i)) {
880
+ this.validateField(i, result);
881
+ }
882
+ });
883
+ return result;
884
+ }
885
+ validateField(field, messages) {
886
+ if (!this.fieldTypesReader.hasExistingType(field)) {
887
+ messages.set(field, `Unknown field type '${field.type}'`);
888
+ }
889
+ if (field.fieldGroup?.length) {
890
+ field.fieldGroup
891
+ ?.filter((i) => !i.key && this.fieldTypesReader.isFieldArray(i))
892
+ ?.forEach((i) => messages.set(i, 'Field array in a group should have a key'));
893
+ }
894
+ }
895
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DataValidator, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
896
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DataValidator });
897
+ }
898
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: DataValidator, decorators: [{
899
+ type: Injectable
900
+ }] });
901
+
902
+ class InitialValueFactory {
903
+ fieldTypesReader = inject(FieldTypesReader);
904
+ create(fields, defaultArraySize = 0) {
905
+ const result = this.createRecursive(fields, defaultArraySize);
906
+ return defaultArraySize ? result : Array.isArray(result) ? [] : {};
907
+ }
908
+ createRecursive(fields, defaultArraySize) {
909
+ const result = {};
910
+ let arrayResult = null;
911
+ fields.forEach((i) => {
912
+ const value = this.createRecursiveForField(i, defaultArraySize);
913
+ if (i.key) {
914
+ result[`${i.key}`] = value;
915
+ }
916
+ else if (typeof value === 'object') {
917
+ Array.isArray(value)
918
+ ? (arrayResult = value)
919
+ : Object.assign(result, value);
920
+ }
921
+ });
922
+ return Object.keys(result).length ? result : arrayResult;
923
+ }
924
+ createRecursiveForField(field, defaultArraySize) {
925
+ if (this.fieldTypesReader.isFieldGroup(field) && field.fieldGroup) {
926
+ return this.createRecursive(field.fieldGroup, defaultArraySize);
927
+ }
928
+ if (this.fieldTypesReader.isFieldArray(field)) {
929
+ if (defaultArraySize === 0) {
930
+ return [];
931
+ }
932
+ const fieldGroup = typeof field.fieldArray === 'object'
933
+ ? field.fieldArray.fieldGroup
934
+ : null;
935
+ return fieldGroup
936
+ ? [this.createRecursive(fieldGroup, defaultArraySize)]
937
+ : [null];
938
+ }
939
+ return null;
940
+ }
941
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: InitialValueFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
942
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: InitialValueFactory });
943
+ }
944
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: InitialValueFactory, decorators: [{
945
+ type: Injectable
946
+ }] });
947
+
948
+ class ResultFormComponent {
949
+ overlayFields = signal([], ...(ngDevMode ? [{ debugName: "overlayFields" }] : /* istanbul ignore next */ []));
950
+ form = new FormGroup({});
951
+ formlyFields = signal([], ...(ngDevMode ? [{ debugName: "formlyFields" }] : /* istanbul ignore next */ []));
952
+ model = signal(undefined, ...(ngDevMode ? [{ debugName: "model" }] : /* istanbul ignore next */ []));
953
+ elementRef = inject(ElementRef);
954
+ changeDetectorRef = inject(ChangeDetectorRef);
955
+ dataManager = inject(DataManager);
956
+ selection = inject((SelectionManager));
957
+ marker = inject(FieldMarker);
958
+ dataValidator = inject(DataValidator);
959
+ initialValueFactory = inject(InitialValueFactory);
960
+ formRenderConfig = inject(EditorConfigReader).formRenderConfig;
961
+ changeDectorRef = inject(ChangeDetectorRef);
962
+ destroyRef = inject(DestroyRef);
963
+ ngOnInit() {
964
+ fromEvent(window, 'resize')
965
+ .pipe(takeUntilDestroyed(this.destroyRef), debounceTime(200))
966
+ .subscribe(() => this.render(this.dataManager.getFields()));
967
+ this.dataManager.onChange.subscribe((fields) => this.render(fields));
968
+ this.selection.onSelect.subscribe(() => this.changeDectorRef.detectChanges());
969
+ }
970
+ isSelected(field) {
971
+ return this.selection.has(field);
972
+ }
973
+ handleClick(field, event) {
974
+ this.selection.select(field, event.ctrlKey, event.shiftKey);
975
+ }
976
+ overlayFieldTrackBy(_index, item) {
977
+ return item.field;
978
+ }
979
+ render(fields) {
980
+ const fieldByIdx = new Map(toFlatArray(fields).map((i, idx) => [`${idx}`, i]));
981
+ const formlyFields = structuredClone(fields);
982
+ const validationMessages = this.dataValidator.validate(formlyFields);
983
+ toFlatArray(formlyFields).forEach((i, idx) => {
984
+ this.formRenderConfig.editFormFieldConverter(i);
985
+ this.marker.markField(i, `${idx}`);
986
+ const validationMessage = validationMessages.get(i);
987
+ if (validationMessage) {
988
+ this.markInvalidField(i, validationMessage);
989
+ }
990
+ });
991
+ this.clearForm(this.initialValueFactory.create(formlyFields, 1));
992
+ this.formlyFields.set(formlyFields);
993
+ this.changeDetectorRef.detectChanges();
994
+ const rect = this.elementRef.nativeElement.getBoundingClientRect();
995
+ this.overlayFields.set(Object.values(this.elementRef.nativeElement.getElementsByTagName('formly-field'))
996
+ .map((i) => ({
997
+ rect: this.calculateRect(i, rect),
998
+ field: fieldByIdx.get(this.marker.getIdFromElement(i)),
999
+ }))
1000
+ .filter((i) => i.field));
1001
+ }
1002
+ markInvalidField(field, message) {
1003
+ field.type = 'formly-template';
1004
+ field.template = message;
1005
+ if (field.fieldArray) {
1006
+ delete field.fieldArray;
1007
+ }
1008
+ if (field.fieldGroup) {
1009
+ delete field.fieldGroup;
1010
+ }
1011
+ }
1012
+ clearForm(value) {
1013
+ Object.keys(this.form.controls).forEach((i) => this.form.removeControl(i));
1014
+ this.form.reset(value);
1015
+ this.model.set(value);
1016
+ }
1017
+ calculateRect(element, container) {
1018
+ const result = {
1019
+ left: Number.MAX_VALUE,
1020
+ top: Number.MAX_VALUE,
1021
+ right: 0,
1022
+ bottom: 0,
1023
+ };
1024
+ [element]
1025
+ .concat(Object.values(element.querySelectorAll('*')))
1026
+ .forEach((i) => {
1027
+ const rect = i.getBoundingClientRect();
1028
+ if (rect.width && rect.height) {
1029
+ result.left = Math.min(rect.left, result.left);
1030
+ result.top = Math.min(rect.top, result.top);
1031
+ result.right = Math.max(rect.right, result.right);
1032
+ result.bottom = Math.max(rect.bottom, result.bottom);
1033
+ }
1034
+ });
1035
+ return {
1036
+ left: Math.max(result.left - container.left, 0),
1037
+ top: Math.max(result.top - container.top, 0),
1038
+ right: Math.min(container.right - result.right, container.width),
1039
+ bottom: Math.min(container.bottom - result.bottom, container.height),
1040
+ };
1041
+ }
1042
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ResultFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1043
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: ResultFormComponent, isStandalone: true, selector: "formly-designer-result-form", ngImport: i0, template: "<form [formGroup]=\"form\">\n <formly-form\n [fields]=\"formlyFields()\"\n [form]=\"form\"\n [model]=\"model()\"\n inert=\"true\"\n />\n</form>\n<div class=\"overlay\">\n @for (field of overlayFields(); track overlayFieldTrackBy($index, field)) {\n <div\n [class]=\"'overlay_field' + (isSelected(field.field) ? '--selected' : '')\"\n [style.left]=\"field.rect.left + 'px'\"\n [style.top]=\"field.rect.top + 'px'\"\n [style.right]=\"field.rect.right + 'px'\"\n [style.bottom]=\"field.rect.bottom + 'px'\"\n (click)=\"handleClick(field.field, $event)\"\n ></div>\n }\n</div>\n", styles: [":host{position:relative;display:block}.overlay{position:absolute;z-index:1;cursor:pointer;inset:0;opacity:.1}.overlay_field,.overlay_field--selected{position:absolute}.overlay_field--selected{opacity:1;background-color:gray}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1044
+ }
1045
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ResultFormComponent, decorators: [{
1046
+ type: Component,
1047
+ args: [{ selector: 'formly-designer-result-form', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, FormlyForm], template: "<form [formGroup]=\"form\">\n <formly-form\n [fields]=\"formlyFields()\"\n [form]=\"form\"\n [model]=\"model()\"\n inert=\"true\"\n />\n</form>\n<div class=\"overlay\">\n @for (field of overlayFields(); track overlayFieldTrackBy($index, field)) {\n <div\n [class]=\"'overlay_field' + (isSelected(field.field) ? '--selected' : '')\"\n [style.left]=\"field.rect.left + 'px'\"\n [style.top]=\"field.rect.top + 'px'\"\n [style.right]=\"field.rect.right + 'px'\"\n [style.bottom]=\"field.rect.bottom + 'px'\"\n (click)=\"handleClick(field.field, $event)\"\n ></div>\n }\n</div>\n", styles: [":host{position:relative;display:block}.overlay{position:absolute;z-index:1;cursor:pointer;inset:0;opacity:.1}.overlay_field,.overlay_field--selected{position:absolute}.overlay_field--selected{opacity:1;background-color:gray}\n"] }]
1048
+ }] });
1049
+
1050
+ class ResultStructureComponent {
1051
+ nodes;
1052
+ fieldTypes = [];
1053
+ formlyFieldTypes = inject(FieldTypesReader);
1054
+ dataManager = inject(DataManager);
1055
+ dataValidator = inject(DataValidator);
1056
+ selection = inject((SelectionManager));
1057
+ validationMessages;
1058
+ changeDectorRef = inject(ChangeDetectorRef);
1059
+ ngOnInit() {
1060
+ this.fieldTypes = this.formlyFieldTypes.get();
1061
+ this.nodes = this.dataManager.onChange.pipe(map((fields) => {
1062
+ this.validationMessages = this.dataValidator.validate(fields);
1063
+ return this.toDragDropNodes(fields);
1064
+ }));
1065
+ this.selection.onSelect.subscribe(() => this.changeDectorRef.detectChanges());
1066
+ }
1067
+ handleDropped(event) {
1068
+ this.dataManager.moveField(event.currentArray[event.currentIndex].data, event.targetIndex, event.targetParent?.data);
1069
+ }
1070
+ handleClick(field, event) {
1071
+ this.selection.select(field, event.ctrlKey, event.shiftKey);
1072
+ }
1073
+ isSelected(field) {
1074
+ return this.selection.has(field);
1075
+ }
1076
+ getFieldType(field) {
1077
+ return this.formlyFieldTypes.getType(field);
1078
+ }
1079
+ treeTrackBy(_index, item) {
1080
+ return item.data;
1081
+ }
1082
+ getWarning(field) {
1083
+ return this.validationMessages?.get(field);
1084
+ }
1085
+ toDragDropNodes(fields) {
1086
+ return fields.map((i) => {
1087
+ if (this.formlyFieldTypes.isFieldGroup(i)) {
1088
+ return {
1089
+ data: i,
1090
+ childNodes: this.toDragDropNodes(i.fieldGroup || []),
1091
+ };
1092
+ }
1093
+ if (this.formlyFieldTypes.isFieldArray(i)) {
1094
+ return {
1095
+ data: i,
1096
+ childNodes: typeof i.fieldArray === 'object'
1097
+ ? this.toDragDropNodes([i.fieldArray])
1098
+ : [],
1099
+ };
1100
+ }
1101
+ return { data: i };
1102
+ });
1103
+ }
1104
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ResultStructureComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1105
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: ResultStructureComponent, isStandalone: true, selector: "formly-designer-result-structure", ngImport: i0, template: "<matx-drag-drop-tree\n [nodes]=\"(nodes | async) ?? []\"\n [trackBy]=\"treeTrackBy\"\n (dropped)=\"handleDropped($event)\"\n class=\"tree\"\n>\n <ng-template #headerTemplate let-data=\"data\">\n <div\n (click)=\"handleClick(data, $event)\"\n [class]=\"'tree-node-header' + (isSelected(data) ? '--selected' : '')\"\n >\n <strong title=\"type\">[{{ getFieldType(data) }}]</strong>\n <span title=\"key\"> {{ data?.key }} </span>\n <em title=\"label\">{{ data?.props?.label }}</em>\n @if (getWarning(data)) {\n <span class=\"icon-container\">\n &nbsp;\n <mat-icon [title]=\"getWarning(data)\" color=\"warn\">warning</mat-icon>\n </span>\n }\n </div>\n </ng-template>\n</matx-drag-drop-tree>\n", styles: [".tree-node-header,.tree-node-header--selected{padding:5px;border-radius:4px;background-color:#fffc}.tree-node-header em,.tree-node-header--selected em{color:gray}.tree-node-header .icon-container,.tree-node-header--selected .icon-container{display:inline-block;width:30px;position:relative}.tree-node-header .icon-container mat-icon,.tree-node-header--selected .icon-container mat-icon{font-size:18px;position:absolute;top:1px;right:-5px}.tree-node-header--selected,.tree-node-header--selected--selected{background-color:#f0f0f0}.tree .tree-node-header{background:none}\n"], dependencies: [{ kind: "component", type: MatxDragDropTree, selector: "matx-drag-drop-tree", inputs: ["nodes", "trackBy"], outputs: ["dropped"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1106
+ }
1107
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ResultStructureComponent, decorators: [{
1108
+ type: Component,
1109
+ args: [{ selector: 'formly-designer-result-structure', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe, MatxDragDropTree, MatIcon], template: "<matx-drag-drop-tree\n [nodes]=\"(nodes | async) ?? []\"\n [trackBy]=\"treeTrackBy\"\n (dropped)=\"handleDropped($event)\"\n class=\"tree\"\n>\n <ng-template #headerTemplate let-data=\"data\">\n <div\n (click)=\"handleClick(data, $event)\"\n [class]=\"'tree-node-header' + (isSelected(data) ? '--selected' : '')\"\n >\n <strong title=\"type\">[{{ getFieldType(data) }}]</strong>\n <span title=\"key\"> {{ data?.key }} </span>\n <em title=\"label\">{{ data?.props?.label }}</em>\n @if (getWarning(data)) {\n <span class=\"icon-container\">\n &nbsp;\n <mat-icon [title]=\"getWarning(data)\" color=\"warn\">warning</mat-icon>\n </span>\n }\n </div>\n </ng-template>\n</matx-drag-drop-tree>\n", styles: [".tree-node-header,.tree-node-header--selected{padding:5px;border-radius:4px;background-color:#fffc}.tree-node-header em,.tree-node-header--selected em{color:gray}.tree-node-header .icon-container,.tree-node-header--selected .icon-container{display:inline-block;width:30px;position:relative}.tree-node-header .icon-container mat-icon,.tree-node-header--selected .icon-container mat-icon{font-size:18px;position:absolute;top:1px;right:-5px}.tree-node-header--selected,.tree-node-header--selected--selected{background-color:#f0f0f0}.tree .tree-node-header{background:none}\n"] }]
1110
+ }] });
1111
+
1112
+ class ResultTestComponent {
1113
+ form = new UntypedFormGroup({});
1114
+ formlyOptions = {};
1115
+ formlyFields = [];
1116
+ model;
1117
+ dataManager = inject(DataManager);
1118
+ initialValueFactory = inject(InitialValueFactory);
1119
+ testValueManager = inject(TestValueManager);
1120
+ formRenderConfig = inject(EditorConfigReader).formRenderConfig;
1121
+ destroyRef = inject(DestroyRef);
1122
+ fields = [];
1123
+ constructor() {
1124
+ this.fields = this.dataManager.getFields();
1125
+ this.reset();
1126
+ }
1127
+ ngAfterViewInit() {
1128
+ this.testValueManager.set(this.model);
1129
+ this.form.valueChanges
1130
+ .pipe(takeUntilDestroyed(this.destroyRef))
1131
+ .subscribe(() => this.testValueManager.set(this.model));
1132
+ }
1133
+ reset() {
1134
+ const value = this.initialValueFactory.create(this.fields);
1135
+ this.model = value;
1136
+ this.form.reset(value);
1137
+ const formlyFields = structuredClone(this.fields);
1138
+ toFlatArray(formlyFields).forEach((i) => this.formRenderConfig.testFormFieldConverter(i));
1139
+ this.formlyFields = formlyFields;
1140
+ this.testValueManager.set(this.model);
1141
+ }
1142
+ validate() {
1143
+ this.form.markAsDirty();
1144
+ this.form.markAllAsTouched();
1145
+ }
1146
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ResultTestComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1147
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.18", type: ResultTestComponent, isStandalone: true, selector: "formly-designer-result-test", ngImport: i0, template: "<formly-form\n [form]=\"form\"\n [fields]=\"formlyFields\"\n [options]=\"formlyOptions\"\n [model]=\"model\"\n/>\n<div class=\"button-container\">\n <button mat-stroked-button (click)=\"reset()\">Clear</button>\n <button mat-stroked-button (click)=\"validate()\">Validate</button>\n</div>\n", styles: [".button-container{text-align:right}\n"], dependencies: [{ 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: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1148
+ }
1149
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ResultTestComponent, decorators: [{
1150
+ type: Component,
1151
+ args: [{ selector: 'formly-designer-result-test', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatButton, FormlyForm], template: "<formly-form\n [form]=\"form\"\n [fields]=\"formlyFields\"\n [options]=\"formlyOptions\"\n [model]=\"model\"\n/>\n<div class=\"button-container\">\n <button mat-stroked-button (click)=\"reset()\">Clear</button>\n <button mat-stroked-button (click)=\"validate()\">Validate</button>\n</div>\n", styles: [".button-container{text-align:right}\n"] }]
1152
+ }], ctorParameters: () => [] });
1153
+
1154
+ class ResultComponent {
1155
+ mode = model('select', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1156
+ data = signal('', ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
1157
+ formatter = (data) => {
1158
+ const result = jsonParse(data);
1159
+ return result.success ? jsonStringify(result.result) : data;
1160
+ };
1161
+ dataManager = inject(DataManager);
1162
+ ngAfterContentInit() {
1163
+ this.dataManager.onChange.subscribe((fields) => this.data.set(JSON.stringify(fields)));
1164
+ }
1165
+ setMode(mode) {
1166
+ this.mode.set(mode);
1167
+ }
1168
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ResultComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1169
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: ResultComponent, isStandalone: true, selector: "formly-designer-result", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { mode: "modeChange" }, ngImport: i0, template: "<matx-panel headerTitle=\"Form\">\n <matx-panel-header-button\n icon=\"highlight_alt\"\n tooltip=\"Select fields\"\n (click)=\"setMode('select')\"\n [selected]=\"mode() === 'select'\"\n />\n <matx-panel-header-button\n icon=\"density_small\"\n tooltip=\"Structure\"\n (click)=\"setMode('structure')\"\n [selected]=\"mode() === 'structure'\"\n />\n <matx-panel-header-button\n icon=\"code\"\n tooltip=\"View code\"\n (click)=\"setMode('code')\"\n [selected]=\"mode() === 'code'\"\n />\n <matx-panel-header-button\n icon=\"play_arrow\"\n tooltip=\"Test form\"\n (click)=\"setMode('test')\"\n [selected]=\"mode() === 'test'\"\n />\n @switch (mode()) {\n @case (\"structure\") {\n <formly-designer-result-structure />\n }\n @case (\"code\") {\n <b-code-editor\n [disabled]=\"true\"\n [data]=\"data()\"\n [formatter]=\"formatter\"\n />\n }\n @case (\"select\") {\n <formly-designer-result-form />\n }\n @case (\"test\") {\n <formly-designer-result-test />\n }\n }\n</matx-panel>\n", dependencies: [{ kind: "component", type: ResultFormComponent, selector: "formly-designer-result-form" }, { kind: "component", type: ResultStructureComponent, selector: "formly-designer-result-structure" }, { kind: "component", type: ResultTestComponent, selector: "formly-designer-result-test" }, { kind: "component", type: MatxPanel, selector: "matx-panel", inputs: ["headerTitle", "fullHeight"] }, { kind: "component", type: MatxPanelHeaderButton, selector: "matx-panel-header-button", inputs: ["icon", "tooltip", "selected", "disabled"] }, { kind: "component", type: CodeEditor, selector: "b-code-editor", inputs: ["data", "disabled", "formatter", "validator"], outputs: ["validChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1170
+ }
1171
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ResultComponent, decorators: [{
1172
+ type: Component,
1173
+ args: [{ selector: 'formly-designer-result', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
1174
+ ResultFormComponent,
1175
+ ResultStructureComponent,
1176
+ ResultTestComponent,
1177
+ MatxPanel,
1178
+ MatxPanelHeaderButton,
1179
+ CodeEditor,
1180
+ ], template: "<matx-panel headerTitle=\"Form\">\n <matx-panel-header-button\n icon=\"highlight_alt\"\n tooltip=\"Select fields\"\n (click)=\"setMode('select')\"\n [selected]=\"mode() === 'select'\"\n />\n <matx-panel-header-button\n icon=\"density_small\"\n tooltip=\"Structure\"\n (click)=\"setMode('structure')\"\n [selected]=\"mode() === 'structure'\"\n />\n <matx-panel-header-button\n icon=\"code\"\n tooltip=\"View code\"\n (click)=\"setMode('code')\"\n [selected]=\"mode() === 'code'\"\n />\n <matx-panel-header-button\n icon=\"play_arrow\"\n tooltip=\"Test form\"\n (click)=\"setMode('test')\"\n [selected]=\"mode() === 'test'\"\n />\n @switch (mode()) {\n @case (\"structure\") {\n <formly-designer-result-structure />\n }\n @case (\"code\") {\n <b-code-editor\n [disabled]=\"true\"\n [data]=\"data()\"\n [formatter]=\"formatter\"\n />\n }\n @case (\"select\") {\n <formly-designer-result-form />\n }\n @case (\"test\") {\n <formly-designer-result-test />\n }\n }\n</matx-panel>\n" }]
1181
+ }], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }, { type: i0.Output, args: ["modeChange"] }] } });
1182
+
1183
+ class EditorFieldsReader {
1184
+ fieldTypesReader = inject(FieldTypesReader);
1185
+ fieldsByKey;
1186
+ keysByType;
1187
+ constructor() {
1188
+ const config = inject(EditorConfigReader);
1189
+ this.fieldsByKey = config.properties;
1190
+ this.keysByType = config.propertiesByType;
1191
+ }
1192
+ getFields(type) {
1193
+ const result = [];
1194
+ if (!this.keysByType[type]) {
1195
+ console.warn(`Editor config reader: No fields for type ${type} found`);
1196
+ }
1197
+ (this.keysByType[type] ?? []).forEach((i) => {
1198
+ if (!this.fieldsByKey[i]) {
1199
+ console.warn(`Editor config reader: Field with key ${i} for type ${type} not found`);
1200
+ return;
1201
+ }
1202
+ const field = structuredClone(this.fieldsByKey[i]);
1203
+ if (field.key === 'type' && !field.props?.options) {
1204
+ field.props ??= {};
1205
+ field.props.options = this.getOptions(type);
1206
+ }
1207
+ if (field.key === 'props.menu' && !field.props?.options) {
1208
+ field.props ??= {};
1209
+ }
1210
+ result.push(field);
1211
+ });
1212
+ return [{ type: 'formly-editor-group', fieldGroup: result }];
1213
+ }
1214
+ getOptions(type) {
1215
+ if (this.fieldTypesReader.isFieldArray({ type: type })) {
1216
+ return this.fieldTypesReader
1217
+ .getArrayTypes()
1218
+ .map((i) => ({ value: i, label: i }));
1219
+ }
1220
+ if (this.fieldTypesReader.isFieldGroup({ type: type })) {
1221
+ return this.fieldTypesReader
1222
+ .getGroupTypes()
1223
+ .map((i) => ({ value: i, label: i }));
1224
+ }
1225
+ return this.fieldTypesReader
1226
+ .getStandardTypes()
1227
+ .map((i) => ({ value: i, label: i }));
1228
+ }
1229
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: EditorFieldsReader, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1230
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: EditorFieldsReader });
1231
+ }
1232
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: EditorFieldsReader, decorators: [{
1233
+ type: Injectable
1234
+ }], ctorParameters: () => [] });
1235
+
1236
+ class FormlyFieldEditorEdit {
1237
+ form = new UntypedFormGroup({});
1238
+ fields = signal([], ...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
1239
+ formField = signal(null, ...(ngDevMode ? [{ debugName: "formField" }] : /* istanbul ignore next */ []));
1240
+ editorFieldsReader = inject(EditorFieldsReader);
1241
+ fieldTypesReader = inject(FieldTypesReader);
1242
+ selectionManager = inject((SelectionManager));
1243
+ dataManager = inject(DataManager);
1244
+ destroyRef = inject(DestroyRef);
1245
+ field;
1246
+ ngOnInit() {
1247
+ this.form.valueChanges
1248
+ .pipe(takeUntilDestroyed(this.destroyRef), debounceTime(200))
1249
+ .subscribe(() => this.save());
1250
+ this.selectionManager.onSelect
1251
+ .pipe(takeUntilDestroyed(this.destroyRef))
1252
+ .subscribe((field) => {
1253
+ if (this.field !== field && field !== null) {
1254
+ this.save();
1255
+ this.field = field;
1256
+ this.updateForm(field);
1257
+ this.setValue(field);
1258
+ }
1259
+ });
1260
+ }
1261
+ save() {
1262
+ if (this.form.pristine) {
1263
+ return;
1264
+ }
1265
+ const formField = this.formField();
1266
+ if (this.field && formField) {
1267
+ this.form.markAsPristine();
1268
+ const currentField = this.field;
1269
+ this.field = structuredClone(formField);
1270
+ cleanInput(this.field);
1271
+ this.dataManager.updateField(currentField, this.field);
1272
+ if (currentField.type !== this.field.type) {
1273
+ this.updateForm(this.field);
1274
+ }
1275
+ }
1276
+ }
1277
+ setValue(field) {
1278
+ field = structuredClone(field);
1279
+ field.props ??= {};
1280
+ this.formField.set(field);
1281
+ }
1282
+ updateForm(field) {
1283
+ Object.keys(this.form.controls).forEach((i) => this.form.removeControl(i));
1284
+ this.fields.set(this.editorFieldsReader.getFields(this.fieldTypesReader.getType(field)));
1285
+ }
1286
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyFieldEditorEdit, deps: [], target: i0.ɵɵFactoryTarget.Component });
1287
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.18", type: FormlyFieldEditorEdit, isStandalone: true, selector: "formly-designer-field-editor-edit", ngImport: i0, template: `
1288
+ <form [formGroup]="form">
1289
+ <matx-full-width-form-fields>
1290
+ <formly-form [fields]="fields()" [form]="form" [model]="formField()" />
1291
+ </matx-full-width-form-fields>
1292
+ </form>
1293
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormlyForm, selector: "formly-form", inputs: ["form", "model", "fields", "options"], outputs: ["modelChange"] }, { kind: "component", type: MatxFullWidthFormFields, selector: "matx-full-width-form-fields" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1294
+ }
1295
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyFieldEditorEdit, decorators: [{
1296
+ type: Component,
1297
+ args: [{
1298
+ selector: 'formly-designer-field-editor-edit',
1299
+ template: `
1300
+ <form [formGroup]="form">
1301
+ <matx-full-width-form-fields>
1302
+ <formly-form [fields]="fields()" [form]="form" [model]="formField()" />
1303
+ </matx-full-width-form-fields>
1304
+ </form>
1305
+ `,
1306
+ changeDetection: ChangeDetectionStrategy.OnPush,
1307
+ imports: [ReactiveFormsModule, FormlyForm, MatxFullWidthFormFields],
1308
+ }]
1309
+ }] });
1310
+
1311
+ class FormlyFieldEditorCode {
1312
+ data = model('', ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
1313
+ formatter = (data) => {
1314
+ const result = jsonParse(data);
1315
+ return result.success ? jsonStringify(result.result) : data;
1316
+ };
1317
+ validator = (data) => this.validate(data);
1318
+ dataManager = inject(DataManager);
1319
+ fieldTypes = inject(FieldTypesReader);
1320
+ selection = inject((SelectionManager));
1321
+ destroyRef = inject(DestroyRef);
1322
+ jsonEditorChild = viewChild.required(CodeEditor);
1323
+ field;
1324
+ ngOnInit() {
1325
+ this.selection.onSelect
1326
+ .pipe(takeUntilDestroyed(this.destroyRef))
1327
+ .subscribe((field) => {
1328
+ if (this.field !== field && field !== null) {
1329
+ this.save();
1330
+ this.field = field;
1331
+ if (field.fieldArray || field.fieldGroup) {
1332
+ field = { ...field };
1333
+ delete field.fieldGroup;
1334
+ delete field.fieldArray;
1335
+ }
1336
+ this.data.set(JSON.stringify(field));
1337
+ }
1338
+ });
1339
+ }
1340
+ save() {
1341
+ const data = this.jsonEditorChild().getData();
1342
+ if (!data || !this.validator(data).valid) {
1343
+ return;
1344
+ }
1345
+ const currentField = this.field;
1346
+ this.field = JSON5.parse(data);
1347
+ this.field.fieldGroup = currentField.fieldGroup;
1348
+ this.field.fieldArray = currentField.fieldArray;
1349
+ cleanInput(this.field);
1350
+ this.dataManager.updateField(currentField, this.field);
1351
+ }
1352
+ validate(data) {
1353
+ const result = jsonParseFieldsAndValidate(data);
1354
+ if (!result.success ||
1355
+ !(result.result[0].fieldArray || result.result[0].fieldGroup)) {
1356
+ return { valid: result.success, message: result.message };
1357
+ }
1358
+ const field = result.result[0];
1359
+ if (field.fieldArray && !this.fieldTypes.isFieldArray(field)) {
1360
+ return {
1361
+ valid: false,
1362
+ message: `Can not add /fieldArray to field type '${this.fieldTypes.getType(field)}'.`,
1363
+ };
1364
+ }
1365
+ if (field.fieldGroup && !this.fieldTypes.isFieldGroup(field)) {
1366
+ return {
1367
+ valid: false,
1368
+ message: `Can not add /fieldGroup to field type '${this.fieldTypes.getType(field)}'.`,
1369
+ };
1370
+ }
1371
+ return {
1372
+ valid: false,
1373
+ message: "Add fields to /fieldArray or /fieldGroup with 'Add field'.",
1374
+ };
1375
+ }
1376
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyFieldEditorCode, deps: [], target: i0.ɵɵFactoryTarget.Component });
1377
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.18", type: FormlyFieldEditorCode, isStandalone: true, selector: "formly-designer-field-editor-code", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { data: "dataChange" }, viewQueries: [{ propertyName: "jsonEditorChild", first: true, predicate: CodeEditor, descendants: true, isSignal: true }], ngImport: i0, template: `<b-code-editor
1378
+ [data]="data()"
1379
+ [formatter]="formatter"
1380
+ [validator]="validator"
1381
+ (validChange)="save()"
1382
+ />`, isInline: true, styles: [":host{display:flex;flex-direction:column;flex:1;overflow:hidden}b-code-editor{flex:1;overflow:hidden}\n"], dependencies: [{ kind: "component", type: CodeEditor, selector: "b-code-editor", inputs: ["data", "disabled", "formatter", "validator"], outputs: ["validChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1383
+ }
1384
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyFieldEditorCode, decorators: [{
1385
+ type: Component,
1386
+ args: [{ selector: 'formly-designer-field-editor-code', template: `<b-code-editor
1387
+ [data]="data()"
1388
+ [formatter]="formatter"
1389
+ [validator]="validator"
1390
+ (validChange)="save()"
1391
+ />`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CodeEditor], styles: [":host{display:flex;flex-direction:column;flex:1;overflow:hidden}b-code-editor{flex:1;overflow:hidden}\n"] }]
1392
+ }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }, { type: i0.Output, args: ["dataChange"] }], jsonEditorChild: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CodeEditor), { isSignal: true }] }] } });
1393
+
1394
+ class FormlyFieldEditorAdd {
1395
+ types = [];
1396
+ optionSelected = output();
1397
+ formControl;
1398
+ filteredTypes;
1399
+ fieldTypesReader = inject(FieldTypesReader);
1400
+ editorConfigReader = inject(EditorConfigReader);
1401
+ dataManager = inject(DataManager);
1402
+ commitHandled = false;
1403
+ ngOnInit() {
1404
+ this.types = this.fieldTypesReader
1405
+ .get()
1406
+ .filter((i) => this.editorConfigReader.propertiesByType[i])
1407
+ .sort((x, y) => x.localeCompare(y));
1408
+ this.formControl = new FormControl('', this.inListValidator(this.types));
1409
+ this.filteredTypes = this.formControl.valueChanges.pipe(startWith(''), map((value) => {
1410
+ value = value.toLowerCase();
1411
+ return this.types.filter((option) => option.toLowerCase().includes(value));
1412
+ }));
1413
+ }
1414
+ handleFocusout() {
1415
+ this.formControl.markAsUntouched();
1416
+ }
1417
+ handleClick() {
1418
+ this.commitHandled = false;
1419
+ }
1420
+ handleSelect(event) {
1421
+ event.option.deselect();
1422
+ this.select(event.option.value);
1423
+ this.commitHandled = true;
1424
+ }
1425
+ commit() {
1426
+ if (this.commitHandled) {
1427
+ this.commitHandled = false;
1428
+ return;
1429
+ }
1430
+ const value = (this.formControl.value ?? '').toLowerCase();
1431
+ const selection = this.types.filter((i) => i.toLowerCase() === value);
1432
+ if (selection.length) {
1433
+ this.select(selection[0]);
1434
+ }
1435
+ else {
1436
+ this.formControl.markAsTouched();
1437
+ }
1438
+ }
1439
+ select(value) {
1440
+ const field = { type: value };
1441
+ this.dataManager.addFields([field]);
1442
+ this.formControl.reset('');
1443
+ }
1444
+ inListValidator(items) {
1445
+ return (control) => {
1446
+ const value = control.value?.toLowerCase();
1447
+ const valid = items.some((i) => i.toLowerCase() === value);
1448
+ return valid ? null : { notInList: true };
1449
+ };
1450
+ }
1451
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyFieldEditorAdd, deps: [], target: i0.ɵɵFactoryTarget.Component });
1452
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FormlyFieldEditorAdd, isStandalone: true, selector: "formly-designer-field-editor-add", outputs: { optionSelected: "optionSelected" }, host: { listeners: { "focusout": "handleFocusout()" } }, ngImport: i0, template: `
1453
+ <mat-form-field floatLabel="always">
1454
+ <mat-label>type</mat-label>
1455
+ <input
1456
+ matInput
1457
+ [matAutocomplete]="autocomplete"
1458
+ [formControl]="formControl"
1459
+ (keydown.enter)="commit()"
1460
+ />
1461
+ <mat-autocomplete
1462
+ autoActiveFirstOption
1463
+ #autocomplete="matAutocomplete"
1464
+ (optionSelected)="handleSelect($event)"
1465
+ >
1466
+ @for (option of filteredTypes | async; track option) {
1467
+ <mat-option [value]="option" (click)="handleClick()">
1468
+ {{ option }}
1469
+ </mat-option>
1470
+ }
1471
+ </mat-autocomplete>
1472
+ <mat-error>Select value from list</mat-error>
1473
+ </mat-form-field>
1474
+ <button mat-stroked-button color="primary" (click)="commit()">
1475
+ <mat-icon>add</mat-icon>
1476
+ Add field
1477
+ </button>
1478
+ `, isInline: true, styles: ["mat-form-field{margin-right:20px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { 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: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i2.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i2.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1479
+ }
1480
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyFieldEditorAdd, decorators: [{
1481
+ type: Component,
1482
+ args: [{ selector: 'formly-designer-field-editor-add', template: `
1483
+ <mat-form-field floatLabel="always">
1484
+ <mat-label>type</mat-label>
1485
+ <input
1486
+ matInput
1487
+ [matAutocomplete]="autocomplete"
1488
+ [formControl]="formControl"
1489
+ (keydown.enter)="commit()"
1490
+ />
1491
+ <mat-autocomplete
1492
+ autoActiveFirstOption
1493
+ #autocomplete="matAutocomplete"
1494
+ (optionSelected)="handleSelect($event)"
1495
+ >
1496
+ @for (option of filteredTypes | async; track option) {
1497
+ <mat-option [value]="option" (click)="handleClick()">
1498
+ {{ option }}
1499
+ </mat-option>
1500
+ }
1501
+ </mat-autocomplete>
1502
+ <mat-error>Select value from list</mat-error>
1503
+ </mat-form-field>
1504
+ <button mat-stroked-button color="primary" (click)="commit()">
1505
+ <mat-icon>add</mat-icon>
1506
+ Add field
1507
+ </button>
1508
+ `, host: {
1509
+ '(focusout)': 'handleFocusout()',
1510
+ }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
1511
+ AsyncPipe,
1512
+ ReactiveFormsModule,
1513
+ MatButton,
1514
+ MatIcon,
1515
+ MatInput,
1516
+ MatAutocompleteModule,
1517
+ MatFormFieldModule,
1518
+ ], styles: ["mat-form-field{margin-right:20px}\n"] }]
1519
+ }], propDecorators: { optionSelected: [{ type: i0.Output, args: ["optionSelected"] }] } });
1520
+
1521
+ class FormlyFieldEditor {
1522
+ mode = signal('add', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1523
+ selection = inject((SelectionManager));
1524
+ snackBar = inject(MatSnackBar);
1525
+ field = null;
1526
+ editChild = viewChild.required(FormlyFieldEditorEdit);
1527
+ codeChild = viewChild.required(FormlyFieldEditorCode);
1528
+ ngOnInit() {
1529
+ this.selection.onSelect.subscribe((field) => {
1530
+ this.field = field;
1531
+ if (field === null) {
1532
+ this.mode.set('add');
1533
+ }
1534
+ else if (this.mode() === 'add') {
1535
+ this.mode.set('edit');
1536
+ }
1537
+ });
1538
+ }
1539
+ save() {
1540
+ this.mode() === 'edit' && this.editChild().save();
1541
+ this.mode() === 'code' && this.codeChild().save();
1542
+ }
1543
+ setMode(mode) {
1544
+ if (this.field === null && (mode === 'edit' || mode === 'code')) {
1545
+ this.snackBar.open('Select a field to edit', 'X');
1546
+ return;
1547
+ }
1548
+ if (mode === 'edit' || mode === 'code') {
1549
+ this.save();
1550
+ }
1551
+ this.mode.set(mode);
1552
+ }
1553
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyFieldEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
1554
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FormlyFieldEditor, isStandalone: true, selector: "formly-designer-field-editor", viewQueries: [{ propertyName: "editChild", first: true, predicate: FormlyFieldEditorEdit, descendants: true, isSignal: true }, { propertyName: "codeChild", first: true, predicate: FormlyFieldEditorCode, descendants: true, isSignal: true }], ngImport: i0, template: "<matx-panel headerTitle=\"Field\">\n <matx-panel-header-button\n icon=\"add\"\n tooltip=\"Add field\"\n (click)=\"setMode('add')\"\n [selected]=\"mode() === 'add'\"\n />\n <matx-panel-header-button\n icon=\"edit\"\n tooltip=\"Edit field\"\n (click)=\"setMode('edit')\"\n [selected]=\"mode() === 'edit'\"\n />\n <matx-panel-header-button\n icon=\"code\"\n tooltip=\"Code\"\n (click)=\"setMode('code')\"\n [selected]=\"mode() === 'code'\"\n />\n @if (mode() === \"add\") {\n <formly-designer-field-editor-add />\n }\n @if (mode() === \"edit\") {\n <formly-designer-field-editor-edit />\n }\n @if (mode() === \"code\") {\n <formly-designer-field-editor-code />\n }\n</matx-panel>\n", dependencies: [{ kind: "component", type: FormlyFieldEditorEdit, selector: "formly-designer-field-editor-edit" }, { kind: "component", type: FormlyFieldEditorCode, selector: "formly-designer-field-editor-code", inputs: ["data"], outputs: ["dataChange"] }, { kind: "component", type: FormlyFieldEditorAdd, selector: "formly-designer-field-editor-add", outputs: ["optionSelected"] }, { kind: "component", type: MatxPanel, selector: "matx-panel", inputs: ["headerTitle", "fullHeight"] }, { kind: "component", type: MatxPanelHeaderButton, selector: "matx-panel-header-button", inputs: ["icon", "tooltip", "selected", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1555
+ }
1556
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyFieldEditor, decorators: [{
1557
+ type: Component,
1558
+ args: [{ selector: 'formly-designer-field-editor', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
1559
+ FormlyFieldEditorEdit,
1560
+ FormlyFieldEditorCode,
1561
+ FormlyFieldEditorAdd,
1562
+ MatxPanel,
1563
+ MatxPanelHeaderButton,
1564
+ ], template: "<matx-panel headerTitle=\"Field\">\n <matx-panel-header-button\n icon=\"add\"\n tooltip=\"Add field\"\n (click)=\"setMode('add')\"\n [selected]=\"mode() === 'add'\"\n />\n <matx-panel-header-button\n icon=\"edit\"\n tooltip=\"Edit field\"\n (click)=\"setMode('edit')\"\n [selected]=\"mode() === 'edit'\"\n />\n <matx-panel-header-button\n icon=\"code\"\n tooltip=\"Code\"\n (click)=\"setMode('code')\"\n [selected]=\"mode() === 'code'\"\n />\n @if (mode() === \"add\") {\n <formly-designer-field-editor-add />\n }\n @if (mode() === \"edit\") {\n <formly-designer-field-editor-edit />\n }\n @if (mode() === \"code\") {\n <formly-designer-field-editor-code />\n }\n</matx-panel>\n" }]
1565
+ }], propDecorators: { editChild: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FormlyFieldEditorEdit), { isSignal: true }] }], codeChild: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FormlyFieldEditorCode), { isSignal: true }] }] } });
1566
+
1567
+ class TestValueComponent {
1568
+ data;
1569
+ formatter = (data) => {
1570
+ const result = jsonParse(data);
1571
+ return result.success ? jsonStringify(result.result) : data;
1572
+ };
1573
+ constructor() {
1574
+ this.data = inject(TestValueManager).onChange.pipe(map((value) => JSON.stringify(value ?? {})));
1575
+ }
1576
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TestValueComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1577
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.18", type: TestValueComponent, isStandalone: true, selector: "formly-designer-test-value", ngImport: i0, template: "<matx-panel headerTitle=\"Value\">\n <b-code-editor\n [disabled]=\"true\"\n [data]=\"(data | async) ?? ''\"\n [formatter]=\"formatter\"\n />\n</matx-panel>\n", dependencies: [{ kind: "component", type: MatxPanel, selector: "matx-panel", inputs: ["headerTitle", "fullHeight"] }, { kind: "component", type: CodeEditor, selector: "b-code-editor", inputs: ["data", "disabled", "formatter", "validator"], outputs: ["validChange"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1578
+ }
1579
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TestValueComponent, decorators: [{
1580
+ type: Component,
1581
+ args: [{ selector: 'formly-designer-test-value', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AsyncPipe, MatxPanel, CodeEditor], template: "<matx-panel headerTitle=\"Value\">\n <b-code-editor\n [disabled]=\"true\"\n [data]=\"(data | async) ?? ''\"\n [formatter]=\"formatter\"\n />\n</matx-panel>\n" }]
1582
+ }], ctorParameters: () => [] });
1583
+
1584
+ class EditCommand {
1585
+ commandType;
1586
+ data;
1587
+ constructor(commandType, data) {
1588
+ this.commandType = commandType;
1589
+ this.data = data;
1590
+ }
1591
+ }
1592
+ var EditCommandType;
1593
+ (function (EditCommandType) {
1594
+ EditCommandType[EditCommandType["EditUndo"] = 0] = "EditUndo";
1595
+ EditCommandType[EditCommandType["EditRedo"] = 1] = "EditRedo";
1596
+ EditCommandType[EditCommandType["EditCut"] = 2] = "EditCut";
1597
+ EditCommandType[EditCommandType["EditCopy"] = 3] = "EditCopy";
1598
+ EditCommandType[EditCommandType["EditPaste"] = 4] = "EditPaste";
1599
+ EditCommandType[EditCommandType["EditDelete"] = 5] = "EditDelete";
1600
+ EditCommandType[EditCommandType["EditSelectAll"] = 6] = "EditSelectAll";
1601
+ })(EditCommandType || (EditCommandType = {}));
1602
+
1603
+ class FormlyEditor {
1604
+ hidden = input(false, ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
1605
+ fields = input([], ...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
1606
+ onCommand = input(...(ngDevMode ? [undefined, { debugName: "onCommand" }] : /* istanbul ignore next */ []));
1607
+ dataChange = output();
1608
+ fieldEditor = viewChild.required(FormlyFieldEditor);
1609
+ mode = 'select';
1610
+ snackBar = inject(MatSnackBar);
1611
+ dataManager = inject(DataManager);
1612
+ undoManager = inject((UndoManager));
1613
+ selection = inject((SelectionManager));
1614
+ destroyRef = inject(DestroyRef);
1615
+ ngOnInit() {
1616
+ this.onCommand()
1617
+ ?.pipe(takeUntilDestroyed(this.destroyRef))
1618
+ .subscribe((i) => this.handleCommand(i));
1619
+ this.dataManager.setFields(this.fields());
1620
+ this.dataManager.onChange
1621
+ .pipe(skip(1))
1622
+ .subscribe(() => this.dataChange.emit());
1623
+ }
1624
+ handleModeChange(mode) {
1625
+ this.mode = mode;
1626
+ }
1627
+ handleCommand(command) {
1628
+ if (this.hidden()) {
1629
+ return;
1630
+ }
1631
+ this.fieldEditor().save();
1632
+ switch (command.commandType) {
1633
+ case EditCommandType.EditUndo:
1634
+ this.undoManager.undo();
1635
+ break;
1636
+ case EditCommandType.EditRedo:
1637
+ this.undoManager.redo();
1638
+ break;
1639
+ case EditCommandType.EditCut:
1640
+ navigator.clipboard.writeText(this.getSelectedFieldsJson());
1641
+ this.dataManager.removeSelectedFields();
1642
+ break;
1643
+ case EditCommandType.EditCopy:
1644
+ navigator.clipboard.writeText(this.getSelectedFieldsJson());
1645
+ break;
1646
+ case EditCommandType.EditPaste:
1647
+ const convertedData = jsonParseFieldsAndValidate(command.data ?? '');
1648
+ if (convertedData.success) {
1649
+ this.dataManager.addFields(convertedData.result);
1650
+ }
1651
+ else {
1652
+ console.warn(convertedData.message, { data: command.data });
1653
+ this.snackBar.open('Clipboard contains invalid data', 'X');
1654
+ }
1655
+ break;
1656
+ case EditCommandType.EditDelete:
1657
+ this.dataManager.removeSelectedFields();
1658
+ break;
1659
+ case EditCommandType.EditSelectAll:
1660
+ this.selection.set(toFlatArray(this.dataManager.getFields()));
1661
+ break;
1662
+ }
1663
+ }
1664
+ getSelectedFieldsJson() {
1665
+ return jsonStringify(this.dataManager.getSelectedFields());
1666
+ }
1667
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
1668
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: FormlyEditor, isStandalone: true, selector: "formly-editor", inputs: { hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, onCommand: { classPropertyName: "onCommand", publicName: "onCommand", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dataChange: "dataChange" }, host: { properties: { "class.hidden": "hidden()" } }, providers: [SelectionManager, DataManager, TestValueManager, UndoManager], viewQueries: [{ propertyName: "fieldEditor", first: true, predicate: FormlyFieldEditor, descendants: true, isSignal: true }], ngImport: i0, template: "@if (!hidden()) {\n <div class=\"result\">\n <formly-designer-result\n [mode]=\"mode\"\n (modeChange)=\"handleModeChange($event)\"\n />\n </div>\n <div [class]=\"mode === 'test' ? 'editor--hidden' : 'editor'\">\n <formly-designer-field-editor />\n </div>\n @if (mode === \"test\") {\n <div class=\"test-value\">\n <formly-designer-test-value />\n </div>\n }\n}\n", styles: [":host{display:flex}:host.hidden{display:none}.result,.editor,.editor--hidden,.test-value{box-sizing:border-box}.editor--hidden{display:none}.result{min-width:0;max-width:1000px;flex:2;margin-right:1px}.editor,.test-value{min-width:0;max-width:500px;flex:1}\n"], dependencies: [{ kind: "component", type: FormlyFieldEditor, selector: "formly-designer-field-editor" }, { kind: "component", type: ResultComponent, selector: "formly-designer-result", inputs: ["mode"], outputs: ["modeChange"] }, { kind: "component", type: TestValueComponent, selector: "formly-designer-test-value" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1669
+ }
1670
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: FormlyEditor, decorators: [{
1671
+ type: Component,
1672
+ args: [{ selector: 'formly-editor', providers: [SelectionManager, DataManager, TestValueManager, UndoManager], host: {
1673
+ '[class.hidden]': 'hidden()',
1674
+ }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormlyFieldEditor, ResultComponent, TestValueComponent], template: "@if (!hidden()) {\n <div class=\"result\">\n <formly-designer-result\n [mode]=\"mode\"\n (modeChange)=\"handleModeChange($event)\"\n />\n </div>\n <div [class]=\"mode === 'test' ? 'editor--hidden' : 'editor'\">\n <formly-designer-field-editor />\n </div>\n @if (mode === \"test\") {\n <div class=\"test-value\">\n <formly-designer-test-value />\n </div>\n }\n}\n", styles: [":host{display:flex}:host.hidden{display:none}.result,.editor,.editor--hidden,.test-value{box-sizing:border-box}.editor--hidden{display:none}.result{min-width:0;max-width:1000px;flex:2;margin-right:1px}.editor,.test-value{min-width:0;max-width:500px;flex:1}\n"] }]
1675
+ }], propDecorators: { hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: false }] }], onCommand: [{ type: i0.Input, args: [{ isSignal: true, alias: "onCommand", required: false }] }], dataChange: [{ type: i0.Output, args: ["dataChange"] }], fieldEditor: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FormlyFieldEditor), { isSignal: true }] }] } });
1676
+
1677
+ class JsonExporter {
1678
+ get name() {
1679
+ return 'Json';
1680
+ }
1681
+ get extension() {
1682
+ return '.json';
1683
+ }
1684
+ export(data) {
1685
+ return jsonStringify(data);
1686
+ }
1687
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: JsonExporter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1688
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: JsonExporter, providedIn: 'root' });
1689
+ }
1690
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: JsonExporter, decorators: [{
1691
+ type: Injectable,
1692
+ args: [{ providedIn: 'root' }]
1693
+ }] });
1694
+
1695
+ function provideFormlyEditor(config) {
1696
+ return [
1697
+ {
1698
+ provide: FORMLY_EDITOR_CONFIG,
1699
+ useValue: config,
1700
+ },
1701
+ EditorConfigReader,
1702
+ EditorFieldsReader,
1703
+ FieldTypesReader,
1704
+ DataValidator,
1705
+ InitialValueFactory,
1706
+ ];
1707
+ }
1708
+
1709
+ /**
1710
+ * Generated bundle index. Do not edit.
1711
+ */
1712
+
1713
+ export { EditCommand, EditCommandType, FormlyEditor, JsonExporter, provideFormlyEditor };
1714
+ //# sourceMappingURL=grumptech-ngx-formly-editor.mjs.map