@ngx-formly/core 10.0.1 → 11.1.0-rc.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.
@@ -1,857 +1,685 @@
1
- import * as i0 from '@angular/core';
2
- import { Injectable } from '@angular/core';
3
- import { UntypedFormArray, UntypedFormGroup } from '@angular/forms';
4
- import { ɵhasKey as _hasKey, ɵgetFieldValue as _getFieldValue, ɵclone as _clone, ɵreverseDeepMerge as _reverseDeepMerge } from '@ngx-formly/core';
5
- import { tap } from 'rxjs/operators';
6
-
7
- // https://stackoverflow.com/a/27865285
1
+ import * as i0 from "@angular/core";
2
+ import { Injectable } from "@angular/core";
3
+ import { UntypedFormArray, UntypedFormGroup } from "@angular/forms";
4
+ import { ɵclone, ɵgetFieldValue, ɵhasKey, ɵreverseDeepMerge } from "@ngx-formly/core";
5
+ import { tap } from "rxjs/operators";
8
6
  function decimalPlaces(a) {
9
- if (!isFinite(a)) {
10
- return 0;
11
- }
12
- let e = 1, p = 0;
13
- while (Math.round(a * e) / e !== a) {
14
- e *= 10;
15
- p++;
16
- }
17
- return p;
7
+ if (!isFinite(a)) return 0;
8
+ let e = 1, p = 0;
9
+ while (Math.round(a * e) / e !== a) {
10
+ e *= 10;
11
+ p++;
12
+ }
13
+ return p;
18
14
  }
19
15
  function isEmpty(v) {
20
- return v === '' || v == null;
16
+ return v === "" || v == null;
21
17
  }
22
18
  function isObject(v) {
23
- return v != null && typeof v === 'object' && !Array.isArray(v);
19
+ return v != null && typeof v === "object" && !Array.isArray(v);
24
20
  }
25
21
  function isInteger(value) {
26
- return Number.isInteger ? Number.isInteger(value) : typeof value === 'number' && Math.floor(value) === value;
22
+ return Number.isInteger ? Number.isInteger(value) : typeof value === "number" && Math.floor(value) === value;
27
23
  }
28
24
  function isConst(schema) {
29
- return typeof schema === 'object' && (schema.hasOwnProperty('const') || (schema.enum && schema.enum.length === 1));
25
+ return typeof schema === "object" && (schema.hasOwnProperty("const") || schema.enum && schema.enum.length === 1);
30
26
  }
31
27
  function toNumber(value) {
32
- if (value === '' || value === undefined) {
33
- return undefined;
34
- }
35
- if (value === null) {
36
- return null;
37
- }
38
- if (typeof value === 'number') {
39
- return value;
40
- }
41
- const val = parseFloat(value);
42
- return !isNaN(val) ? val : value;
28
+ if (value === "" || value === void 0) return;
29
+ if (value === null) return null;
30
+ if (typeof value === "number") return value;
31
+ const val = parseFloat(value);
32
+ return !isNaN(val) ? val : value;
43
33
  }
44
34
  function totalMatchedFields(field) {
45
- if (!field.fieldGroup) {
46
- return _hasKey(field) && _getFieldValue(field) !== undefined ? 1 : 0;
47
- }
48
- const total = field.fieldGroup.reduce((s, f) => totalMatchedFields(f) + s, 0);
49
- if (total === 0 && _hasKey(field)) {
50
- const value = _getFieldValue(field);
51
- if (value === null ||
52
- (value !== undefined && ((field.fieldArray && Array.isArray(value)) || (!field.fieldArray && isObject(value))))) {
53
- return 1;
54
- }
55
- }
56
- return total;
57
- }
58
- class FormlyJsonschema {
59
- toFieldConfig(schema, options) {
60
- schema = _clone(schema);
61
- return this._toFieldConfig(schema, { schema, ...(options || {}) });
62
- }
63
- _toFieldConfig(schema, { key, isOptional, ...options }) {
64
- schema = this.resolveSchema(schema, options);
65
- const types = this.guessSchemaType(schema);
66
- let field = {
67
- type: types[0],
68
- defaultValue: schema.default,
69
- props: {
70
- label: schema.title,
71
- readonly: schema.readOnly,
72
- description: schema.description,
73
- },
74
- };
75
- if (key != null) {
76
- field.key = key;
77
- }
78
- if (!options.ignoreDefault && (schema.readOnly || options.readOnly)) {
79
- field.props.disabled = true;
80
- options = { ...options, readOnly: true };
81
- }
82
- if (options.resetOnHide) {
83
- field.resetOnHide = true;
84
- }
85
- if (options.shareFormControl === false) {
86
- field.shareFormControl = false;
87
- }
88
- if (field.defaultValue === undefined && types.length === 1 && isOptional === false) {
89
- switch (types[0]) {
90
- case 'null': {
91
- field.defaultValue = null;
92
- break;
93
- }
94
- case 'string': {
95
- if (!options.ignoreImplicitStringDefaults) {
96
- field.defaultValue = '';
97
- }
98
- break;
99
- }
100
- case 'object': {
101
- field.defaultValue = {};
102
- break;
103
- }
104
- case 'array': {
105
- field.defaultValue = schema.minItems > 0 ? Array.from(new Array(schema.minItems)) : [];
106
- break;
107
- }
108
- }
109
- }
110
- if (options.ignoreDefault) {
111
- delete field.defaultValue;
112
- }
113
- this.addValidator(field, 'type', {
114
- schemaType: types,
115
- expression: ({ value }) => {
116
- if (value === undefined) {
117
- return true;
118
- }
119
- if (value === null && types.indexOf('null') !== -1) {
120
- return true;
121
- }
122
- switch (types[0]) {
123
- case 'null': {
124
- return typeof value === null;
125
- }
126
- case 'string': {
127
- return typeof value === 'string';
128
- }
129
- case 'integer': {
130
- return isInteger(value);
131
- }
132
- case 'number': {
133
- return typeof value === 'number';
134
- }
135
- case 'object': {
136
- return isObject(value);
137
- }
138
- case 'array': {
139
- return Array.isArray(value);
140
- }
141
- }
142
- return true;
143
- },
144
- });
145
- switch (field.type) {
146
- case 'number':
147
- case 'integer': {
148
- field.parsers = [
149
- (v, f) => {
150
- v = v === '' && types.includes('null') ? null : toNumber(v);
151
- if (v === null && f) {
152
- const input = typeof document !== 'undefined' && f.id
153
- ? document.querySelector(`#${f.id}`)
154
- : undefined;
155
- if (input && input.validity && !input.validity.badInput && !types.includes('null')) {
156
- v = undefined;
157
- }
158
- if (v !== f.formControl.value) {
159
- f.formControl.setValue(v, { emitModelToViewChange: false });
160
- }
161
- }
162
- return v;
163
- },
164
- ];
165
- if (schema.hasOwnProperty('minimum')) {
166
- field.props.min = schema.minimum;
167
- }
168
- if (schema.hasOwnProperty('maximum')) {
169
- field.props.max = schema.maximum;
170
- }
171
- if (schema.hasOwnProperty('exclusiveMinimum')) {
172
- field.props.exclusiveMinimum = schema.exclusiveMinimum;
173
- this.addValidator(field, 'exclusiveMinimum', ({ value }) => isEmpty(value) || value > schema.exclusiveMinimum);
174
- }
175
- if (schema.hasOwnProperty('exclusiveMaximum')) {
176
- field.props.exclusiveMaximum = schema.exclusiveMaximum;
177
- this.addValidator(field, 'exclusiveMaximum', ({ value }) => isEmpty(value) || value < schema.exclusiveMaximum);
178
- }
179
- if (schema.hasOwnProperty('multipleOf')) {
180
- field.props.step = schema.multipleOf;
181
- this.addValidator(field, 'multipleOf', ({ value }) => {
182
- if (isEmpty(value) || typeof value !== 'number' || value === 0 || schema.multipleOf <= 0) {
183
- return true;
184
- }
185
- // https://github.com/ajv-validator/ajv/issues/652#issue-283610859
186
- const multiplier = Math.pow(10, decimalPlaces(schema.multipleOf));
187
- return Math.round(value * multiplier) % Math.round(schema.multipleOf * multiplier) === 0;
188
- });
189
- }
190
- break;
191
- }
192
- case 'string': {
193
- field.parsers = [
194
- (v, f) => {
195
- if (types.indexOf('null') !== -1) {
196
- v = isEmpty(v) ? null : v;
197
- }
198
- else if (f && !f.props.required) {
199
- v = v === '' ? undefined : v;
200
- }
201
- return v;
202
- },
203
- ];
204
- ['minLength', 'maxLength', 'pattern'].forEach((prop) => {
205
- if (schema.hasOwnProperty(prop)) {
206
- field.props[prop] = schema[prop];
207
- }
208
- });
209
- break;
210
- }
211
- case 'object': {
212
- if (!field.fieldGroup) {
213
- field.fieldGroup = [];
214
- }
215
- const { propDeps, schemaDeps } = this.resolveDependencies(schema);
216
- Object.keys(schema.properties || {}).forEach((property) => {
217
- const isRequired = Array.isArray(schema.required) && schema.required.indexOf(property) !== -1;
218
- const f = this._toFieldConfig(schema.properties[property], {
219
- ...options,
220
- key: property,
221
- isOptional: isOptional || !isRequired,
222
- });
223
- field.fieldGroup.push(f);
224
- if (isRequired || propDeps[property]) {
225
- f.expressions = {
226
- ...(f.expressions || {}),
227
- 'props.required': (f) => {
228
- let parent = f.parent;
229
- const model = f.fieldGroup && f.key != null ? parent.model : f.model;
230
- while (parent.key == null && parent.parent) {
231
- parent = parent.parent;
232
- }
233
- const required = parent && parent.props ? parent.props.required : false;
234
- if (!model && !required) {
235
- return false;
236
- }
237
- if (Array.isArray(schema.required) && schema.required.indexOf(property) !== -1) {
238
- return true;
239
- }
240
- return propDeps[property] && f.model && propDeps[property].some((k) => !isEmpty(f.model[k]));
241
- },
242
- };
243
- }
244
- if (schemaDeps[property]) {
245
- const getConstValue = (s) => {
246
- return s.hasOwnProperty('const') ? s.const : s.enum[0];
247
- };
248
- const oneOfSchema = schemaDeps[property].oneOf;
249
- if (oneOfSchema &&
250
- oneOfSchema.every((o) => o.properties && o.properties[property] && isConst(o.properties[property]))) {
251
- oneOfSchema.forEach((oneOfSchemaItem) => {
252
- const { [property]: constSchema, ...properties } = oneOfSchemaItem.properties;
253
- field.fieldGroup.push({
254
- ...this._toFieldConfig({ ...oneOfSchemaItem, properties }, { ...options, shareFormControl: false, resetOnHide: true }),
255
- expressions: {
256
- hide: (f) => !f.model || getConstValue(constSchema) !== f.model[property],
257
- },
258
- });
259
- });
260
- }
261
- else {
262
- field.fieldGroup.push({
263
- ...this._toFieldConfig(schemaDeps[property], options),
264
- expressions: {
265
- hide: (f) => !f.model || isEmpty(f.model[property]),
266
- },
267
- });
268
- }
269
- }
270
- });
271
- if (schema.oneOf) {
272
- field.fieldGroup.push(this.resolveMultiSchema('oneOf', schema.oneOf, { ...options, shareFormControl: false }));
273
- }
274
- if (schema.anyOf) {
275
- field.fieldGroup.push(this.resolveMultiSchema('anyOf', schema.anyOf, options));
276
- }
277
- // Process if/then/else conditional schemas
278
- if (options.conditionalSchemas) {
279
- const conditionalSchemas = options.conditionalSchemas;
280
- conditionalSchemas.forEach((conditionalSchema) => {
281
- const condition = conditionalSchema._ifCondition;
282
- if (condition && conditionalSchema.properties) {
283
- // Create a field group with the conditional fields
284
- const conditionalFieldGroup = {
285
- fieldGroup: [],
286
- expressions: {
287
- hide: (f) => {
288
- if (!f.model) {
289
- return true;
290
- }
291
- const modelValue = f.model[condition.property];
292
- const matches = modelValue === condition.value;
293
- // If negate is true (for "else" case), invert the condition
294
- return condition.negate ? matches : !matches;
295
- },
296
- },
297
- };
298
- // Add each property from the conditional schema to the field group
299
- Object.keys(conditionalSchema.properties).forEach((property) => {
300
- const propSchema = conditionalSchema.properties[property];
301
- if (!propSchema) {
302
- return;
303
- }
304
- const isRequired = Array.isArray(conditionalSchema.required) && conditionalSchema.required.indexOf(property) !== -1;
305
- const conditionalField = this._toFieldConfig(propSchema, {
306
- ...options,
307
- key: property,
308
- isOptional: !isRequired,
309
- resetOnHide: true,
310
- });
311
- conditionalFieldGroup.fieldGroup.push(conditionalField);
312
- });
313
- field.fieldGroup.push(conditionalFieldGroup);
314
- }
315
- });
316
- }
317
- break;
318
- }
319
- case 'array': {
320
- if (schema.hasOwnProperty('minItems')) {
321
- field.props.minItems = schema.minItems;
322
- this.addValidator(field, 'minItems', ({ value }) => {
323
- return isEmpty(value) || value.length >= schema.minItems;
324
- });
325
- if (!isOptional && schema.minItems > 0 && field.defaultValue === undefined) {
326
- field.defaultValue = Array.from(new Array(schema.minItems));
327
- }
328
- }
329
- if (schema.hasOwnProperty('maxItems')) {
330
- field.props.maxItems = schema.maxItems;
331
- this.addValidator(field, 'maxItems', ({ value }) => {
332
- return isEmpty(value) || value.length <= schema.maxItems;
333
- });
334
- }
335
- if (schema.hasOwnProperty('uniqueItems')) {
336
- field.props.uniqueItems = schema.uniqueItems;
337
- this.addValidator(field, 'uniqueItems', ({ value }) => {
338
- if (isEmpty(value) || !schema.uniqueItems) {
339
- return true;
340
- }
341
- const uniqueItems = Array.from(new Set(value.map((v) => JSON.stringify(v, (k, o) => {
342
- if (isObject(o)) {
343
- return Object.keys(o)
344
- .sort()
345
- .reduce((obj, key) => {
346
- obj[key] = o[key];
347
- return obj;
348
- }, {});
349
- }
350
- return o;
351
- }))));
352
- return uniqueItems.length === value.length;
353
- });
354
- }
355
- // resolve items schema needed for isEnum check
356
- if (schema.items && !Array.isArray(schema.items)) {
357
- schema.items = this.resolveSchema(schema.items, options);
358
- }
359
- // TODO: remove isEnum check once adding an option to skip extension
360
- if (!this.isEnum(schema)) {
361
- field.fieldArray = (root) => {
362
- const length = root.fieldGroup ? root.fieldGroup.length : 0;
363
- const items = schema.items;
364
- if (!Array.isArray(items)) {
365
- if (!items) {
366
- return {};
367
- }
368
- const isMultiSchema = items.oneOf || items.anyOf;
369
- // When items is a single schema, the additionalItems keyword is meaningless, and it should not be used.
370
- const f = this._toFieldConfig(items, isMultiSchema ? { ...options, key: `${length}`, isOptional: false } : { ...options, isOptional: false });
371
- if (isMultiSchema && !_hasKey(f)) {
372
- f.key = null;
373
- }
374
- return f;
375
- }
376
- const itemSchema = items[length] ? items[length] : schema.additionalItems;
377
- const f = itemSchema ? this._toFieldConfig(itemSchema, options) : {};
378
- if (f.props) {
379
- f.props.required = true;
380
- }
381
- if (items[length]) {
382
- f.props.removable = false;
383
- }
384
- return f;
385
- };
386
- }
387
- break;
388
- }
389
- }
390
- if (schema.hasOwnProperty('const')) {
391
- field.props.const = schema.const;
392
- this.addValidator(field, 'const', ({ value }) => value === schema.const);
393
- if (!field.type) {
394
- field.defaultValue = schema.const;
395
- }
396
- }
397
- if (this.isEnum(schema)) {
398
- const enumOptions = this.toEnumOptions(schema);
399
- const multiple = field.type === 'array';
400
- field.type = 'enum';
401
- field.props.multiple = multiple;
402
- field.props.options = enumOptions;
403
- const enumValues = enumOptions.map((o) => o.value);
404
- this.addValidator(field, 'enum', ({ value }) => {
405
- if (value === undefined) {
406
- return true;
407
- }
408
- if (multiple) {
409
- return Array.isArray(value) ? value.every((o) => enumValues.includes(o)) : false;
410
- }
411
- return enumValues.includes(value);
412
- });
413
- }
414
- if (schema.oneOf && !field.type) {
415
- delete field.key;
416
- field.fieldGroup = [
417
- this.resolveMultiSchema('oneOf', schema.oneOf, { ...options, key, shareFormControl: false }),
418
- ];
419
- }
420
- if (schema.anyOf && !field.type) {
421
- delete field.key;
422
- field.fieldGroup = [
423
- this.resolveMultiSchema('anyOf', schema.anyOf, { ...options, key, shareFormControl: false }),
424
- ];
425
- }
426
- // map in possible formlyConfig options from the widget property
427
- if (schema.widget?.formlyConfig) {
428
- field = this.mergeFields(field, schema.widget.formlyConfig);
429
- }
430
- field.templateOptions = field.props;
431
- // if there is a map function passed in, use it to allow the user to
432
- // further customize how fields are being mapped
433
- return options.map ? options.map(field, schema) : field;
434
- }
435
- resolveSchema(schema, options) {
436
- if (schema && schema.$ref) {
437
- schema = this.resolveDefinition(schema, options);
438
- }
439
- if (schema && schema.allOf) {
440
- schema = this.resolveAllOf(schema, options);
441
- }
442
- // Process if/then/else and store conditionals in options for later processing
443
- if (schema && (schema.if || schema.then || schema.else)) {
444
- const conditionalSchemas = this.resolveIfThenElse(schema, options);
445
- if (conditionalSchemas.length > 0) {
446
- // Store conditional schemas in options for processing in _toFieldConfig
447
- options.conditionalSchemas = conditionalSchemas;
448
- }
449
- }
450
- return schema;
451
- }
452
- resolveAllOf({ allOf, ...baseSchema }, options) {
453
- if (!allOf.length) {
454
- throw Error(`allOf array can not be empty ${allOf}.`);
455
- }
456
- return allOf.reduce((base, schema) => {
457
- schema = this.resolveSchema(schema, options);
458
- if (base.required && schema.required) {
459
- base.required = [...base.required, ...schema.required];
460
- }
461
- if (schema.uniqueItems) {
462
- base.uniqueItems = schema.uniqueItems;
463
- }
464
- // resolve to min value
465
- ['maxLength', 'maximum', 'exclusiveMaximum', 'maxItems', 'maxProperties'].forEach((prop) => {
466
- if (!isEmpty(base[prop]) && !isEmpty(schema[prop])) {
467
- base[prop] = base[prop] < schema[prop] ? base[prop] : schema[prop];
468
- }
469
- });
470
- // resolve to max value
471
- ['minLength', 'minimum', 'exclusiveMinimum', 'minItems', 'minProperties'].forEach((prop) => {
472
- if (!isEmpty(base[prop]) && !isEmpty(schema[prop])) {
473
- base[prop] = base[prop] > schema[prop] ? base[prop] : schema[prop];
474
- }
475
- });
476
- return _reverseDeepMerge(base, schema);
477
- }, baseSchema);
478
- }
479
- resolveMultiSchema(mode, schemas, options) {
480
- const isRecursive = this.isRecursiveMultiSchema(schemas, options);
481
- const fieldGroup = schemas.map((s, i) => {
482
- const expressions = {
483
- hide: (f, forceUpdate) => {
484
- if (options.skipMultiSchemaValidation) {
485
- return true;
486
- }
487
- const control = f.parent.parent.fieldGroup[0].formControl;
488
- if (control.value === -1 || forceUpdate) {
489
- let value = f.parent.fieldGroup
490
- .map((f, i) => [f, i, this.isFieldValid(f, i, schemas, options, isRecursive)])
491
- .sort(([f1, i1, f1Valid], [f2, i2, f2Valid]) => {
492
- if (f1Valid !== f2Valid) {
493
- return f2Valid ? 1 : -1;
494
- }
495
- const schemaField1 = isRecursive ? this.getSchemaField(schemas[i1]) || f1 : f1;
496
- const schemaField2 = isRecursive ? this.getSchemaField(schemas[i2]) || f2 : f2;
497
- const matchedFields1 = totalMatchedFields(schemaField1);
498
- const matchedFields2 = totalMatchedFields(schemaField2);
499
- if (matchedFields1 === matchedFields2) {
500
- if (schemaField1.props?.disabled === schemaField2.props?.disabled) {
501
- return 0;
502
- }
503
- return matchedFields2 > matchedFields1 ? 1 : -1;
504
- }
505
- return matchedFields2 > matchedFields1 ? 1 : -1;
506
- })
507
- .map(([, i]) => i);
508
- if (mode === 'anyOf') {
509
- const definedValue = value.filter((i) => totalMatchedFields(f.parent.fieldGroup[i]));
510
- value = definedValue.length > 0 ? definedValue : [value[0] || 0];
511
- }
512
- value = value.length > 0 ? value : [0];
513
- control.setValue(mode === 'anyOf' ? value : value[0]);
514
- }
515
- const hide = Array.isArray(control.value) ? control.value.indexOf(i) === -1 : control.value !== i;
516
- if (isRecursive && !hide) {
517
- this.ensureMultiSchemaField(f, s, options);
518
- }
519
- return hide;
520
- },
521
- };
522
- return isRecursive
523
- ? this.createMultiSchemaField(s, options, expressions)
524
- : {
525
- ...this._toFieldConfig(s, { ...options, resetOnHide: true }),
526
- expressions,
527
- };
528
- });
529
- return {
530
- type: 'multischema',
531
- fieldGroup: [
532
- {
533
- type: 'enum',
534
- defaultValue: -1,
535
- props: {
536
- multiple: mode === 'anyOf',
537
- options: schemas.map((s, i) => ({ label: s.title, value: i, disabled: s.readOnly })),
538
- },
539
- hooks: {
540
- onInit: (f) => f.formControl.valueChanges.pipe(tap(() => f.options.detectChanges(f.parent))),
541
- },
542
- },
543
- {
544
- fieldGroup,
545
- },
546
- ],
547
- };
548
- }
549
- createMultiSchemaField(schema, options, expressions) {
550
- return {
551
- expressions,
552
- hooks: {
553
- onInit: (f) => {
554
- if (f.hide === false) {
555
- this.ensureMultiSchemaField(f, schema, options);
556
- }
557
- },
558
- },
559
- };
560
- }
561
- ensureMultiSchemaField(field, schema, options) {
562
- if (field._jsonSchemaFormlyField) {
563
- return;
564
- }
565
- const expressions = field.expressions;
566
- const hooks = field.hooks;
567
- const mutableField = field;
568
- delete mutableField['formControl'];
569
- delete mutableField['_keyPath'];
570
- Object.keys(field).forEach((prop) => {
571
- delete mutableField[prop];
572
- });
573
- Object.assign(field, this._toFieldConfig(schema, { ...options, resetOnHide: true }));
574
- field.expressions = expressions;
575
- field.hooks = hooks;
576
- Object.defineProperty(field, '_jsonSchemaFormlyField', {
577
- value: true,
578
- configurable: true,
579
- });
580
- field.options?.build(field);
581
- }
582
- getSchemaField(schema) {
583
- return schema._field?.fieldGroup?.[0];
584
- }
585
- isRecursiveMultiSchema(schemas, options) {
586
- const visited = new WeakSet();
587
- const visit = (schema) => {
588
- if (!schema || typeof schema !== 'object') {
589
- return false;
590
- }
591
- if (visited.has(schema)) {
592
- return false;
593
- }
594
- visited.add(schema);
595
- if (schema.$ref) {
596
- const definition = this.getDefinition(schema.$ref, options);
597
- return !!definition && (definition.oneOf === schemas || definition.anyOf === schemas || visit(definition));
598
- }
599
- if (schema.oneOf === schemas || schema.anyOf === schemas) {
600
- return true;
601
- }
602
- const children = [];
603
- if (schema.properties) {
604
- children.push(...Object.values(schema.properties));
605
- }
606
- if (schema.items) {
607
- children.push(...(Array.isArray(schema.items) ? schema.items : [schema.items]));
608
- }
609
- if (schema.additionalItems && typeof schema.additionalItems === 'object') {
610
- children.push(schema.additionalItems);
611
- }
612
- if (schema.allOf) {
613
- children.push(...schema.allOf);
614
- }
615
- if (schema.oneOf && schema.oneOf !== schemas) {
616
- children.push(...schema.oneOf);
617
- }
618
- if (schema.anyOf && schema.anyOf !== schemas) {
619
- children.push(...schema.anyOf);
620
- }
621
- if (schema.dependencies) {
622
- Object.values(schema.dependencies).forEach((dependency) => {
623
- if (dependency && !Array.isArray(dependency)) {
624
- children.push(dependency);
625
- }
626
- });
627
- }
628
- ['not', 'if', 'then', 'else'].forEach((key) => {
629
- const child = schema[key];
630
- if (child && typeof child === 'object') {
631
- children.push(child);
632
- }
633
- });
634
- return children.some(visit);
635
- };
636
- return schemas.some(visit);
637
- }
638
- getDefinition(ref, options) {
639
- const [uri, pointer] = ref.split('#/');
640
- if (uri || !pointer) {
641
- return null;
642
- }
643
- let definition = options.schema;
644
- for (const path of pointer.split('/')) {
645
- if (!definition || typeof definition !== 'object' || !definition.hasOwnProperty(path)) {
646
- return null;
647
- }
648
- definition = definition[path];
649
- }
650
- return typeof definition === 'object' ? definition : null;
651
- }
652
- resolveDefinition(schema, options) {
653
- const [uri, pointer] = schema.$ref.split('#/');
654
- if (uri) {
655
- throw Error(`Remote schemas for ${schema.$ref} not supported yet.`);
656
- }
657
- const definition = !pointer
658
- ? null
659
- : pointer
660
- .split('/')
661
- .reduce((def, path) => (def?.hasOwnProperty(path) ? def[path] : null), options.schema);
662
- if (!definition) {
663
- throw Error(`Cannot find a definition for ${schema.$ref}.`);
664
- }
665
- if (definition.$ref) {
666
- return this.resolveDefinition(definition, options);
667
- }
668
- return {
669
- ...definition,
670
- ...['title', 'description', 'default', 'widget'].reduce((annotation, p) => {
671
- if (schema.hasOwnProperty(p)) {
672
- annotation[p] = schema[p];
673
- }
674
- return annotation;
675
- }, {}),
676
- };
677
- }
678
- resolveDependencies(schema) {
679
- const propDeps = {};
680
- const schemaDeps = {};
681
- Object.keys(schema.dependencies || {}).forEach((prop) => {
682
- const dependency = schema.dependencies[prop];
683
- if (Array.isArray(dependency)) {
684
- // Property dependencies
685
- dependency.forEach((dep) => {
686
- if (!propDeps[dep]) {
687
- propDeps[dep] = [prop];
688
- }
689
- else {
690
- propDeps[dep].push(prop);
691
- }
692
- });
693
- }
694
- else {
695
- // schema dependencies
696
- schemaDeps[prop] = dependency;
697
- }
698
- });
699
- return { propDeps, schemaDeps };
700
- }
701
- extractIfCondition(ifSchema) {
702
- // Extract the property and const value from the "if" schema
703
- // Supports: { "properties": { "propName": { "const": value } } }
704
- if (ifSchema.properties) {
705
- const propName = Object.keys(ifSchema.properties)[0];
706
- if (propName) {
707
- const propSchema = ifSchema.properties[propName];
708
- if (propSchema && propSchema.hasOwnProperty('const')) {
709
- return { property: propName, value: propSchema.const };
710
- }
711
- }
712
- }
713
- return null;
714
- }
715
- resolveIfThenElse(schema, options) {
716
- // Process if/then/else and return array of conditional schemas to add to fieldGroup
717
- const conditionalSchemas = [];
718
- if (schema.if && typeof schema.if === 'object') {
719
- const condition = this.extractIfCondition(schema.if);
720
- if (condition) {
721
- // Process "then" branch
722
- if (schema.then && typeof schema.then === 'object') {
723
- const resolvedSchema = this.resolveConditionalSchema(schema.then, options);
724
- conditionalSchemas.push({
725
- ...resolvedSchema,
726
- _ifCondition: condition,
727
- });
728
- }
729
- // Process "else" branch
730
- if (schema.else && typeof schema.else === 'object') {
731
- const resolvedSchema = this.resolveConditionalSchema(schema.else, options);
732
- conditionalSchemas.push({
733
- ...resolvedSchema,
734
- _ifCondition: { property: condition.property, value: condition.value, negate: true },
735
- });
736
- }
737
- }
738
- }
739
- return conditionalSchemas;
740
- }
741
- resolveConditionalSchema(conditionalSchema, options) {
742
- // Resolve $ref and allOf if present in conditional schema
743
- // Note: We don't use resolveSchema here to avoid recursive processing of nested if/then/else
744
- let resolved = conditionalSchema;
745
- if (conditionalSchema.$ref) {
746
- resolved = this.resolveDefinition(conditionalSchema, options);
747
- }
748
- if (resolved.allOf) {
749
- resolved = this.resolveAllOf(resolved, options);
750
- }
751
- return resolved;
752
- }
753
- guessSchemaType(schema) {
754
- const type = schema?.type;
755
- if (!type && schema?.properties) {
756
- return ['object'];
757
- }
758
- if (Array.isArray(type)) {
759
- if (type.length === 1) {
760
- return type;
761
- }
762
- if (type.length === 2 && type.indexOf('null') !== -1) {
763
- return type.sort((t1) => (t1 == 'null' ? 1 : -1));
764
- }
765
- return type;
766
- }
767
- return type ? [type] : [];
768
- }
769
- addValidator(field, name, validator) {
770
- field.validators = field.validators || {};
771
- field.validators[name] = validator;
772
- }
773
- isEnum(schema) {
774
- return (!!schema.enum ||
775
- (schema.anyOf && schema.anyOf.every(isConst)) ||
776
- (schema.oneOf && schema.oneOf.every(isConst)) ||
777
- (schema.uniqueItems && schema.items && !Array.isArray(schema.items) && this.isEnum(schema.items)));
778
- }
779
- toEnumOptions(schema) {
780
- if (schema.enum) {
781
- return schema.enum.map((value) => ({ value, label: value }));
782
- }
783
- const toEnum = (s) => {
784
- const value = s.hasOwnProperty('const') ? s.const : s.enum[0];
785
- const option = { value, label: s.title || value };
786
- if (s.readOnly) {
787
- option.disabled = true;
788
- }
789
- return option;
790
- };
791
- if (schema.anyOf) {
792
- return schema.anyOf.map(toEnum);
793
- }
794
- if (schema.oneOf) {
795
- return schema.oneOf.map(toEnum);
796
- }
797
- return this.toEnumOptions(schema.items);
798
- }
799
- isFieldValid(root, i, schemas, options, skipMultiSchemaValidation = false) {
800
- const schema = schemas[i];
801
- if (!schema._field) {
802
- Object.defineProperty(schema, '_field', { enumerable: false, writable: true, configurable: true });
803
- }
804
- let field = schema._field;
805
- let model = root.model ? root.model : root.fieldArray ? [] : {};
806
- if (root.model && _hasKey(root)) {
807
- model = { [Array.isArray(root.key) ? root.key.join('.') : root.key]: _getFieldValue(root) };
808
- }
809
- model = _clone(model);
810
- if (!field) {
811
- field = schema._field = root.options.build({
812
- form: Array.isArray(model) ? new UntypedFormArray([]) : new UntypedFormGroup({}),
813
- fieldGroup: [
814
- this._toFieldConfig(schema, {
815
- ...options,
816
- resetOnHide: true,
817
- ignoreDefault: true,
818
- skipMultiSchemaValidation,
819
- map: null,
820
- }),
821
- ],
822
- model,
823
- options: {},
824
- });
825
- }
826
- else {
827
- field.model = model;
828
- root.options.build(field);
829
- }
830
- return field.form.valid;
831
- }
832
- mergeFields(f1, f2) {
833
- for (const prop in f2) {
834
- const f1Prop = prop === 'templateOptions' ? 'props' : prop;
835
- if (isObject(f1[f1Prop]) && isObject(f2[prop])) {
836
- f1[f1Prop] = this.mergeFields(f1[f1Prop], f2[prop]);
837
- }
838
- else if (f2[prop] != null) {
839
- f1[f1Prop] = f2[prop];
840
- }
841
- }
842
- return f1;
843
- }
844
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FormlyJsonschema, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
845
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FormlyJsonschema, providedIn: 'root' }); }
35
+ if (!field.fieldGroup) return ɵhasKey(field) && ɵgetFieldValue(field) !== void 0 ? 1 : 0;
36
+ const total = field.fieldGroup.reduce((s, f) => totalMatchedFields(f) + s, 0);
37
+ if (total === 0 && ɵhasKey(field)) {
38
+ const value = ɵgetFieldValue(field);
39
+ if (value === null || value !== void 0 && (field.fieldArray && Array.isArray(value) || !field.fieldArray && isObject(value))) return 1;
40
+ }
41
+ return total;
846
42
  }
847
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FormlyJsonschema, decorators: [{
848
- type: Injectable,
849
- args: [{ providedIn: 'root' }]
850
- }] });
851
-
852
- /**
853
- * Generated bundle index. Do not edit.
854
- */
855
-
43
+ var FormlyJsonschema = class FormlyJsonschema {
44
+ toFieldConfig(schema, options) {
45
+ schema = ɵclone(schema);
46
+ return this._toFieldConfig(schema, {
47
+ schema,
48
+ ...options || {}
49
+ });
50
+ }
51
+ _toFieldConfig(schema, { key, isOptional, ...options }) {
52
+ schema = this.resolveSchema(schema, options);
53
+ const types = this.guessSchemaType(schema);
54
+ let field = {
55
+ type: types[0],
56
+ defaultValue: schema.default,
57
+ props: {
58
+ label: schema.title,
59
+ readonly: schema.readOnly,
60
+ description: schema.description
61
+ }
62
+ };
63
+ if (key != null) field.key = key;
64
+ if (!options.ignoreDefault && (schema.readOnly || options.readOnly)) {
65
+ field.props.disabled = true;
66
+ options = {
67
+ ...options,
68
+ readOnly: true
69
+ };
70
+ }
71
+ if (options.resetOnHide) field.resetOnHide = true;
72
+ if (options.shareFormControl === false) field.shareFormControl = false;
73
+ if (field.defaultValue === void 0 && types.length === 1 && isOptional === false) switch (types[0]) {
74
+ case "null":
75
+ field.defaultValue = null;
76
+ break;
77
+ case "string":
78
+ if (!options.ignoreImplicitStringDefaults) field.defaultValue = "";
79
+ break;
80
+ case "object":
81
+ field.defaultValue = {};
82
+ break;
83
+ case "array": field.defaultValue = schema.minItems > 0 ? Array.from(new Array(schema.minItems)) : [];
84
+ }
85
+ if (options.ignoreDefault) delete field.defaultValue;
86
+ this.addValidator(field, "type", {
87
+ schemaType: types,
88
+ expression: ({ value }) => {
89
+ if (value === void 0) return true;
90
+ if (value === null && types.indexOf("null") !== -1) return true;
91
+ switch (types[0]) {
92
+ case "null": return false;
93
+ case "string": return typeof value === "string";
94
+ case "integer": return isInteger(value);
95
+ case "number": return typeof value === "number";
96
+ case "object": return isObject(value);
97
+ case "array": return Array.isArray(value);
98
+ }
99
+ return true;
100
+ }
101
+ });
102
+ switch (field.type) {
103
+ case "number":
104
+ case "integer":
105
+ field.parsers = [(v, f) => {
106
+ v = v === "" && types.includes("null") ? null : toNumber(v);
107
+ if (v === null && f) {
108
+ const input = typeof document !== "undefined" && f.id ? document.querySelector(`#${f.id}`) : void 0;
109
+ if (input && input.validity && !input.validity.badInput && !types.includes("null")) v = void 0;
110
+ if (v !== f.formControl.value) f.formControl.setValue(v, { emitModelToViewChange: false });
111
+ }
112
+ return v;
113
+ }];
114
+ if (schema.hasOwnProperty("minimum")) field.props.min = schema.minimum;
115
+ if (schema.hasOwnProperty("maximum")) field.props.max = schema.maximum;
116
+ if (schema.hasOwnProperty("exclusiveMinimum")) {
117
+ field.props.exclusiveMinimum = schema.exclusiveMinimum;
118
+ this.addValidator(field, "exclusiveMinimum", ({ value }) => isEmpty(value) || value > schema.exclusiveMinimum);
119
+ }
120
+ if (schema.hasOwnProperty("exclusiveMaximum")) {
121
+ field.props.exclusiveMaximum = schema.exclusiveMaximum;
122
+ this.addValidator(field, "exclusiveMaximum", ({ value }) => isEmpty(value) || value < schema.exclusiveMaximum);
123
+ }
124
+ if (schema.hasOwnProperty("multipleOf")) {
125
+ field.props.step = schema.multipleOf;
126
+ this.addValidator(field, "multipleOf", ({ value }) => {
127
+ if (isEmpty(value) || typeof value !== "number" || value === 0 || schema.multipleOf <= 0) return true;
128
+ const multiplier = Math.pow(10, decimalPlaces(schema.multipleOf));
129
+ return Math.round(value * multiplier) % Math.round(schema.multipleOf * multiplier) === 0;
130
+ });
131
+ }
132
+ break;
133
+ case "string":
134
+ field.parsers = [(v, f) => {
135
+ if (types.indexOf("null") !== -1) v = isEmpty(v) ? null : v;
136
+ else if (f && !f.props.required) v = v === "" ? void 0 : v;
137
+ return v;
138
+ }];
139
+ [
140
+ "minLength",
141
+ "maxLength",
142
+ "pattern"
143
+ ].forEach((prop) => {
144
+ if (schema.hasOwnProperty(prop)) field.props[prop] = schema[prop];
145
+ });
146
+ break;
147
+ case "object": {
148
+ if (!field.fieldGroup) field.fieldGroup = [];
149
+ const { propDeps, schemaDeps } = this.resolveDependencies(schema);
150
+ Object.keys(schema.properties || {}).forEach((property) => {
151
+ const isRequired = Array.isArray(schema.required) && schema.required.indexOf(property) !== -1;
152
+ const f = this._toFieldConfig(schema.properties[property], {
153
+ ...options,
154
+ key: property,
155
+ isOptional: isOptional || !isRequired
156
+ });
157
+ field.fieldGroup.push(f);
158
+ if (isRequired || propDeps[property]) f.expressions = {
159
+ ...f.expressions || {},
160
+ "props.required": (f) => {
161
+ let parent = f.parent;
162
+ const model = f.fieldGroup && f.key != null ? parent.model : f.model;
163
+ while (parent.key == null && parent.parent) parent = parent.parent;
164
+ const required = parent && parent.props ? parent.props.required : false;
165
+ if (!model && !required) return false;
166
+ if (Array.isArray(schema.required) && schema.required.indexOf(property) !== -1) return true;
167
+ return propDeps[property] && f.model && propDeps[property].some((k) => !isEmpty(f.model[k]));
168
+ }
169
+ };
170
+ if (schemaDeps[property]) {
171
+ const getConstValue = (s) => {
172
+ return s.hasOwnProperty("const") ? s.const : s.enum[0];
173
+ };
174
+ const oneOfSchema = schemaDeps[property].oneOf;
175
+ if (oneOfSchema && oneOfSchema.every((o) => o.properties && o.properties[property] && isConst(o.properties[property]))) oneOfSchema.forEach((oneOfSchemaItem) => {
176
+ const { [property]: constSchema, ...properties } = oneOfSchemaItem.properties;
177
+ field.fieldGroup.push({
178
+ ...this._toFieldConfig({
179
+ ...oneOfSchemaItem,
180
+ properties
181
+ }, {
182
+ ...options,
183
+ shareFormControl: false,
184
+ resetOnHide: true
185
+ }),
186
+ expressions: { hide: (f) => !f.model || getConstValue(constSchema) !== f.model[property] }
187
+ });
188
+ });
189
+ else field.fieldGroup.push({
190
+ ...this._toFieldConfig(schemaDeps[property], options),
191
+ expressions: { hide: (f) => !f.model || isEmpty(f.model[property]) }
192
+ });
193
+ }
194
+ });
195
+ if (schema.oneOf) field.fieldGroup.push(this.resolveMultiSchema("oneOf", schema.oneOf, {
196
+ ...options,
197
+ shareFormControl: false
198
+ }));
199
+ if (schema.anyOf) field.fieldGroup.push(this.resolveMultiSchema("anyOf", schema.anyOf, options));
200
+ if (options.conditionalSchemas) options.conditionalSchemas.forEach((conditionalSchema) => {
201
+ const condition = conditionalSchema._ifCondition;
202
+ if (condition && conditionalSchema.properties) {
203
+ const conditionalFieldGroup = {
204
+ fieldGroup: [],
205
+ expressions: { hide: (f) => {
206
+ if (!f.model) return true;
207
+ const matches = f.model[condition.property] === condition.value;
208
+ return condition.negate ? matches : !matches;
209
+ } }
210
+ };
211
+ Object.keys(conditionalSchema.properties).forEach((property) => {
212
+ const propSchema = conditionalSchema.properties[property];
213
+ if (!propSchema) return;
214
+ const isRequired = Array.isArray(conditionalSchema.required) && conditionalSchema.required.indexOf(property) !== -1;
215
+ const conditionalField = this._toFieldConfig(propSchema, {
216
+ ...options,
217
+ key: property,
218
+ isOptional: !isRequired,
219
+ resetOnHide: true
220
+ });
221
+ conditionalFieldGroup.fieldGroup.push(conditionalField);
222
+ });
223
+ field.fieldGroup.push(conditionalFieldGroup);
224
+ }
225
+ });
226
+ break;
227
+ }
228
+ case "array":
229
+ if (schema.hasOwnProperty("minItems")) {
230
+ field.props.minItems = schema.minItems;
231
+ this.addValidator(field, "minItems", ({ value }) => {
232
+ return isEmpty(value) || value.length >= schema.minItems;
233
+ });
234
+ if (!isOptional && schema.minItems > 0 && field.defaultValue === void 0) field.defaultValue = Array.from(new Array(schema.minItems));
235
+ }
236
+ if (schema.hasOwnProperty("maxItems")) {
237
+ field.props.maxItems = schema.maxItems;
238
+ this.addValidator(field, "maxItems", ({ value }) => {
239
+ return isEmpty(value) || value.length <= schema.maxItems;
240
+ });
241
+ }
242
+ if (schema.hasOwnProperty("uniqueItems")) {
243
+ field.props.uniqueItems = schema.uniqueItems;
244
+ this.addValidator(field, "uniqueItems", ({ value }) => {
245
+ if (isEmpty(value) || !schema.uniqueItems) return true;
246
+ return Array.from(new Set(value.map((v) => JSON.stringify(v, (k, o) => {
247
+ if (isObject(o)) return Object.keys(o).sort().reduce((obj, key) => {
248
+ obj[key] = o[key];
249
+ return obj;
250
+ }, {});
251
+ return o;
252
+ })))).length === value.length;
253
+ });
254
+ }
255
+ if (schema.items && !Array.isArray(schema.items)) schema.items = this.resolveSchema(schema.items, options);
256
+ if (!this.isEnum(schema)) {
257
+ if (Array.isArray(schema.items)) field.props.trackByIndex = true;
258
+ field.fieldArray = (root) => {
259
+ const length = root.fieldGroup ? root.fieldGroup.length : 0;
260
+ const items = schema.items;
261
+ if (!Array.isArray(items)) {
262
+ if (!items) return {};
263
+ const isMultiSchema = items.oneOf || items.anyOf;
264
+ const f = this._toFieldConfig(items, isMultiSchema ? {
265
+ ...options,
266
+ key: `${length}`,
267
+ isOptional: false
268
+ } : {
269
+ ...options,
270
+ isOptional: false
271
+ });
272
+ if (isMultiSchema && !ɵhasKey(f)) f.key = null;
273
+ return f;
274
+ }
275
+ const itemSchema = items[length] ? items[length] : schema.additionalItems;
276
+ const f = itemSchema ? this._toFieldConfig(itemSchema, options) : {};
277
+ if (f.props) f.props.required = true;
278
+ if (items[length]) f.props.removable = false;
279
+ return f;
280
+ };
281
+ }
282
+ }
283
+ if (schema.hasOwnProperty("const")) {
284
+ field.props.const = schema.const;
285
+ this.addValidator(field, "const", ({ value }) => value === schema.const);
286
+ if (!field.type) field.defaultValue = schema.const;
287
+ }
288
+ if (this.isEnum(schema)) {
289
+ const enumOptions = this.toEnumOptions(schema);
290
+ const multiple = field.type === "array";
291
+ field.type = "enum";
292
+ field.props.multiple = multiple;
293
+ field.props.options = enumOptions;
294
+ const enumValues = enumOptions.map((o) => o.value);
295
+ this.addValidator(field, "enum", ({ value }) => {
296
+ if (value === void 0) return true;
297
+ if (multiple) return Array.isArray(value) ? value.every((o) => enumValues.includes(o)) : false;
298
+ return enumValues.includes(value);
299
+ });
300
+ }
301
+ if (schema.oneOf && !field.type) {
302
+ delete field.key;
303
+ field.fieldGroup = [this.resolveMultiSchema("oneOf", schema.oneOf, {
304
+ ...options,
305
+ key,
306
+ shareFormControl: false
307
+ })];
308
+ }
309
+ if (schema.anyOf && !field.type) {
310
+ delete field.key;
311
+ field.fieldGroup = [this.resolveMultiSchema("anyOf", schema.anyOf, {
312
+ ...options,
313
+ key,
314
+ shareFormControl: false
315
+ })];
316
+ }
317
+ if (schema.widget?.formlyConfig) field = this.mergeFields(field, schema.widget.formlyConfig);
318
+ field.templateOptions = field.props;
319
+ return options.map ? options.map(field, schema) : field;
320
+ }
321
+ resolveSchema(schema, options) {
322
+ if (schema && schema.$ref) schema = this.resolveDefinition(schema, options);
323
+ if (schema && schema.allOf) schema = this.resolveAllOf(schema, options);
324
+ if (schema && (schema.if || schema.then || schema.else)) {
325
+ const conditionalSchemas = this.resolveIfThenElse(schema, options);
326
+ if (conditionalSchemas.length > 0) options.conditionalSchemas = conditionalSchemas;
327
+ }
328
+ return schema;
329
+ }
330
+ resolveAllOf({ allOf, ...baseSchema }, options) {
331
+ if (!allOf.length) throw Error(`allOf array can not be empty ${allOf}.`);
332
+ return allOf.reduce((base, schema) => {
333
+ schema = this.resolveSchema(schema, options);
334
+ if (base.required && schema.required) base.required = [...base.required, ...schema.required];
335
+ if (schema.uniqueItems) base.uniqueItems = schema.uniqueItems;
336
+ [
337
+ "maxLength",
338
+ "maximum",
339
+ "exclusiveMaximum",
340
+ "maxItems",
341
+ "maxProperties"
342
+ ].forEach((prop) => {
343
+ if (!isEmpty(base[prop]) && !isEmpty(schema[prop])) base[prop] = base[prop] < schema[prop] ? base[prop] : schema[prop];
344
+ });
345
+ [
346
+ "minLength",
347
+ "minimum",
348
+ "exclusiveMinimum",
349
+ "minItems",
350
+ "minProperties"
351
+ ].forEach((prop) => {
352
+ if (!isEmpty(base[prop]) && !isEmpty(schema[prop])) base[prop] = base[prop] > schema[prop] ? base[prop] : schema[prop];
353
+ });
354
+ return ɵreverseDeepMerge(base, schema);
355
+ }, baseSchema);
356
+ }
357
+ resolveMultiSchema(mode, schemas, options) {
358
+ const isRecursive = this.isRecursiveMultiSchema(schemas, options);
359
+ const fieldGroup = schemas.map((s, i) => {
360
+ const expressions = { hide: (f, forceUpdate) => {
361
+ if (options.skipMultiSchemaValidation) return true;
362
+ const control = f.parent.parent.fieldGroup[0].formControl;
363
+ if (control.value === -1 || forceUpdate) {
364
+ let value = f.parent.fieldGroup.map((f, i) => [
365
+ f,
366
+ i,
367
+ this.isFieldValid(f, i, schemas, options, isRecursive)
368
+ ]).sort(([f1, i1, f1Valid], [f2, i2, f2Valid]) => {
369
+ if (f1Valid !== f2Valid) return f2Valid ? 1 : -1;
370
+ const schemaField1 = isRecursive ? this.getSchemaField(schemas[i1]) || f1 : f1;
371
+ const schemaField2 = isRecursive ? this.getSchemaField(schemas[i2]) || f2 : f2;
372
+ const matchedFields1 = totalMatchedFields(schemaField1);
373
+ const matchedFields2 = totalMatchedFields(schemaField2);
374
+ if (matchedFields1 === matchedFields2) {
375
+ if (schemaField1.props?.disabled === schemaField2.props?.disabled) return 0;
376
+ return matchedFields2 > matchedFields1 ? 1 : -1;
377
+ }
378
+ return matchedFields2 > matchedFields1 ? 1 : -1;
379
+ }).map(([, i]) => i);
380
+ if (mode === "anyOf") {
381
+ const definedValue = value.filter((i) => totalMatchedFields(f.parent.fieldGroup[i]));
382
+ value = definedValue.length > 0 ? definedValue : [value[0] || 0];
383
+ }
384
+ value = value.length > 0 ? value : [0];
385
+ control.setValue(mode === "anyOf" ? value : value[0]);
386
+ }
387
+ const hide = Array.isArray(control.value) ? control.value.indexOf(i) === -1 : control.value !== i;
388
+ if (isRecursive && !hide) this.ensureMultiSchemaField(f, s, options);
389
+ return hide;
390
+ } };
391
+ return isRecursive ? this.createMultiSchemaField(s, options, expressions) : {
392
+ ...this._toFieldConfig(s, {
393
+ ...options,
394
+ resetOnHide: true
395
+ }),
396
+ expressions
397
+ };
398
+ });
399
+ return {
400
+ type: "multischema",
401
+ fieldGroup: [{
402
+ type: "enum",
403
+ defaultValue: -1,
404
+ props: {
405
+ multiple: mode === "anyOf",
406
+ options: schemas.map((s, i) => ({
407
+ label: s.title,
408
+ value: i,
409
+ disabled: s.readOnly
410
+ }))
411
+ },
412
+ hooks: { onInit: (f) => f.formControl.valueChanges.pipe(tap(() => f.options.detectChanges(f.parent))) }
413
+ }, { fieldGroup }]
414
+ };
415
+ }
416
+ createMultiSchemaField(schema, options, expressions) {
417
+ return {
418
+ key: options.key,
419
+ shareFormControl: options.shareFormControl,
420
+ expressions,
421
+ hooks: { onInit: (f) => {
422
+ if (f.hide === false) this.ensureMultiSchemaField(f, schema, options);
423
+ } }
424
+ };
425
+ }
426
+ ensureMultiSchemaField(field, schema, options) {
427
+ if (field._jsonSchemaFormlyField) return;
428
+ const key = field.key;
429
+ const expressions = field.expressions;
430
+ const hooks = field.hooks;
431
+ const mutableField = field;
432
+ delete mutableField["formControl"];
433
+ delete mutableField["_keyPath"];
434
+ Object.keys(field).forEach((prop) => {
435
+ if (prop !== "hide") delete mutableField[prop];
436
+ });
437
+ Object.assign(field, this._toFieldConfig(schema, {
438
+ ...options,
439
+ key,
440
+ resetOnHide: true
441
+ }));
442
+ field.expressions = expressions;
443
+ field.hooks = hooks;
444
+ Object.defineProperty(field, "_jsonSchemaFormlyField", {
445
+ value: true,
446
+ configurable: true
447
+ });
448
+ field.options?.build(field);
449
+ }
450
+ getSchemaField(schema) {
451
+ return schema._field?.fieldGroup?.[0];
452
+ }
453
+ isRecursiveMultiSchema(schemas, options) {
454
+ const visited = /* @__PURE__ */ new WeakSet();
455
+ const visit = (schema) => {
456
+ if (!schema || typeof schema !== "object") return false;
457
+ if (visited.has(schema)) return false;
458
+ visited.add(schema);
459
+ if (schema.$ref) {
460
+ const definition = this.getDefinition(schema.$ref, options);
461
+ return !!definition && (definition.oneOf === schemas || definition.anyOf === schemas || visit(definition));
462
+ }
463
+ if (schema.oneOf === schemas || schema.anyOf === schemas) return true;
464
+ const children = [];
465
+ if (schema.properties) children.push(...Object.values(schema.properties));
466
+ if (schema.items) children.push(...Array.isArray(schema.items) ? schema.items : [schema.items]);
467
+ if (schema.additionalItems && typeof schema.additionalItems === "object") children.push(schema.additionalItems);
468
+ if (schema.allOf) children.push(...schema.allOf);
469
+ if (schema.oneOf && schema.oneOf !== schemas) children.push(...schema.oneOf);
470
+ if (schema.anyOf && schema.anyOf !== schemas) children.push(...schema.anyOf);
471
+ if (schema.dependencies) Object.values(schema.dependencies).forEach((dependency) => {
472
+ if (dependency && !Array.isArray(dependency)) children.push(dependency);
473
+ });
474
+ [
475
+ "not",
476
+ "if",
477
+ "then",
478
+ "else"
479
+ ].forEach((key) => {
480
+ const child = schema[key];
481
+ if (child && typeof child === "object") children.push(child);
482
+ });
483
+ return children.some(visit);
484
+ };
485
+ return schemas.some(visit);
486
+ }
487
+ getDefinition(ref, options) {
488
+ const [uri, pointer] = ref.split("#/");
489
+ if (uri || !pointer) return null;
490
+ let definition = options.schema;
491
+ for (const path of pointer.split("/")) {
492
+ if (!definition || typeof definition !== "object" || !definition.hasOwnProperty(path)) return null;
493
+ definition = definition[path];
494
+ }
495
+ return typeof definition === "object" ? definition : null;
496
+ }
497
+ resolveDefinition(schema, options) {
498
+ const [uri, pointer] = schema.$ref.split("#/");
499
+ if (uri) throw Error(`Remote schemas for ${schema.$ref} not supported yet.`);
500
+ const definition = !pointer ? null : pointer.split("/").reduce((def, path) => def?.hasOwnProperty(path) ? def[path] : null, options.schema);
501
+ if (!definition) throw Error(`Cannot find a definition for ${schema.$ref}.`);
502
+ if (definition.$ref) return this.resolveDefinition(definition, options);
503
+ return {
504
+ ...definition,
505
+ ...[
506
+ "title",
507
+ "description",
508
+ "default",
509
+ "widget"
510
+ ].reduce((annotation, p) => {
511
+ if (schema.hasOwnProperty(p)) annotation[p] = schema[p];
512
+ return annotation;
513
+ }, {})
514
+ };
515
+ }
516
+ resolveDependencies(schema) {
517
+ const propDeps = {};
518
+ const schemaDeps = {};
519
+ Object.keys(schema.dependencies || {}).forEach((prop) => {
520
+ const dependency = schema.dependencies[prop];
521
+ if (Array.isArray(dependency)) dependency.forEach((dep) => {
522
+ if (!propDeps[dep]) propDeps[dep] = [prop];
523
+ else propDeps[dep].push(prop);
524
+ });
525
+ else schemaDeps[prop] = dependency;
526
+ });
527
+ return {
528
+ propDeps,
529
+ schemaDeps
530
+ };
531
+ }
532
+ extractIfCondition(ifSchema) {
533
+ if (ifSchema.properties) {
534
+ const propName = Object.keys(ifSchema.properties)[0];
535
+ if (propName) {
536
+ const propSchema = ifSchema.properties[propName];
537
+ if (propSchema && propSchema.hasOwnProperty("const")) return {
538
+ property: propName,
539
+ value: propSchema.const
540
+ };
541
+ }
542
+ }
543
+ return null;
544
+ }
545
+ resolveIfThenElse(schema, options) {
546
+ const conditionalSchemas = [];
547
+ if (schema.if && typeof schema.if === "object") {
548
+ const condition = this.extractIfCondition(schema.if);
549
+ if (condition) {
550
+ if (schema.then && typeof schema.then === "object") {
551
+ const resolvedSchema = this.resolveConditionalSchema(schema.then, options);
552
+ conditionalSchemas.push({
553
+ ...resolvedSchema,
554
+ _ifCondition: condition
555
+ });
556
+ }
557
+ if (schema.else && typeof schema.else === "object") {
558
+ const resolvedSchema = this.resolveConditionalSchema(schema.else, options);
559
+ conditionalSchemas.push({
560
+ ...resolvedSchema,
561
+ _ifCondition: {
562
+ property: condition.property,
563
+ value: condition.value,
564
+ negate: true
565
+ }
566
+ });
567
+ }
568
+ }
569
+ }
570
+ return conditionalSchemas;
571
+ }
572
+ resolveConditionalSchema(conditionalSchema, options) {
573
+ let resolved = conditionalSchema;
574
+ if (conditionalSchema.$ref) resolved = this.resolveDefinition(conditionalSchema, options);
575
+ if (resolved.allOf) resolved = this.resolveAllOf(resolved, options);
576
+ return resolved;
577
+ }
578
+ guessSchemaType(schema) {
579
+ const type = schema?.type;
580
+ if (!type && schema?.properties) return ["object"];
581
+ if (Array.isArray(type)) {
582
+ if (type.length === 1) return type;
583
+ if (type.length === 2 && type.indexOf("null") !== -1) return type.sort((t1) => t1 == "null" ? 1 : -1);
584
+ return type;
585
+ }
586
+ return type ? [type] : [];
587
+ }
588
+ addValidator(field, name, validator) {
589
+ field.validators = field.validators || {};
590
+ field.validators[name] = validator;
591
+ }
592
+ isEnum(schema) {
593
+ return !!schema.enum || schema.anyOf && schema.anyOf.every(isConst) || schema.oneOf && schema.oneOf.every(isConst) || schema.uniqueItems && schema.items && !Array.isArray(schema.items) && this.isEnum(schema.items);
594
+ }
595
+ toEnumOptions(schema) {
596
+ if (schema.enum) return schema.enum.map((value) => ({
597
+ value,
598
+ label: value
599
+ }));
600
+ const toEnum = (s) => {
601
+ const value = s.hasOwnProperty("const") ? s.const : s.enum[0];
602
+ const option = {
603
+ value,
604
+ label: s.title || value
605
+ };
606
+ if (s.readOnly) option.disabled = true;
607
+ return option;
608
+ };
609
+ if (schema.anyOf) return schema.anyOf.map(toEnum);
610
+ if (schema.oneOf) return schema.oneOf.map(toEnum);
611
+ return this.toEnumOptions(schema.items);
612
+ }
613
+ isFieldValid(root, i, schemas, options, skipMultiSchemaValidation = false) {
614
+ const schema = schemas[i];
615
+ if (!schema._field) Object.defineProperty(schema, "_field", {
616
+ enumerable: false,
617
+ writable: true,
618
+ configurable: true
619
+ });
620
+ const key = ɵhasKey(root) ? root.key : options.key;
621
+ let field = schema._field;
622
+ if (field?.fieldGroup[0].key !== key) field = void 0;
623
+ let model = root.model ? root.model : root.fieldArray ? [] : {};
624
+ if (root.model && ɵhasKey(root)) model = { [Array.isArray(root.key) ? root.key.join(".") : root.key]: ɵgetFieldValue(root) };
625
+ model = ɵclone(model);
626
+ if (!field) field = schema._field = root.options.build({
627
+ form: Array.isArray(model) ? new UntypedFormArray([]) : new UntypedFormGroup({}),
628
+ fieldGroup: [this._toFieldConfig(schema, {
629
+ ...options,
630
+ key,
631
+ resetOnHide: true,
632
+ ignoreDefault: true,
633
+ skipMultiSchemaValidation,
634
+ map: null
635
+ })],
636
+ model,
637
+ options: {}
638
+ });
639
+ else {
640
+ field.model = model;
641
+ root.options.build(field);
642
+ }
643
+ return field.form.valid;
644
+ }
645
+ mergeFields(f1, f2) {
646
+ for (const prop in f2) {
647
+ const f1Prop = prop === "templateOptions" ? "props" : prop;
648
+ if (isObject(f1[f1Prop]) && isObject(f2[prop])) f1[f1Prop] = this.mergeFields(f1[f1Prop], f2[prop]);
649
+ else if (f2[prop] != null) f1[f1Prop] = f2[prop];
650
+ }
651
+ return f1;
652
+ }
653
+ static {
654
+ this.ɵfac = i0.ɵɵngDeclareFactory({
655
+ minVersion: "12.0.0",
656
+ version: "22.2.2",
657
+ ngImport: i0,
658
+ type: FormlyJsonschema,
659
+ deps: [],
660
+ target: i0.ɵɵFactoryTarget.Injectable
661
+ });
662
+ }
663
+ static {
664
+ this.ɵprov = i0.ɵɵngDeclareInjectable({
665
+ minVersion: "12.0.0",
666
+ version: "22.2.2",
667
+ ngImport: i0,
668
+ type: FormlyJsonschema,
669
+ providedIn: "root"
670
+ });
671
+ }
672
+ };
673
+ i0.ɵɵngDeclareClassMetadata({
674
+ minVersion: "12.0.0",
675
+ version: "22.2.2",
676
+ ngImport: i0,
677
+ type: FormlyJsonschema,
678
+ decorators: [{
679
+ type: Injectable,
680
+ args: [{ providedIn: "root" }]
681
+ }]
682
+ });
856
683
  export { FormlyJsonschema };
857
- //# sourceMappingURL=ngx-formly-core-json-schema.mjs.map
684
+
685
+ //# sourceMappingURL=ngx-formly-core-json-schema.mjs.map