@ng-formworks/core 18.5.8 → 18.6.2

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.
Files changed (58) hide show
  1. package/esm2022/lib/json-schema-form.component.mjs +24 -6
  2. package/esm2022/lib/json-schema-form.service.mjs +142 -31
  3. package/esm2022/lib/locale/de-validation-messages.mjs +4 -2
  4. package/esm2022/lib/locale/en-validation-messages.mjs +4 -2
  5. package/esm2022/lib/locale/es-validation-messages.mjs +4 -2
  6. package/esm2022/lib/locale/fr-validation-messages.mjs +4 -2
  7. package/esm2022/lib/locale/it-validation-messages.mjs +4 -2
  8. package/esm2022/lib/locale/pt-validation-messages.mjs +4 -2
  9. package/esm2022/lib/locale/zh-validation-messages.mjs +4 -2
  10. package/esm2022/lib/shared/form-group.functions.mjs +326 -14
  11. package/esm2022/lib/shared/format-regex.constants.mjs +2 -1
  12. package/esm2022/lib/shared/index.mjs +6 -6
  13. package/esm2022/lib/shared/json-schema.functions.mjs +96 -4
  14. package/esm2022/lib/shared/json.validators.mjs +11 -6
  15. package/esm2022/lib/shared/jsonpointer.functions.mjs +2 -2
  16. package/esm2022/lib/shared/layout.functions.mjs +172 -14
  17. package/esm2022/lib/shared/utility.functions.mjs +31 -1
  18. package/esm2022/lib/widget-library/add-reference.component.mjs +20 -16
  19. package/esm2022/lib/widget-library/button.component.mjs +5 -2
  20. package/esm2022/lib/widget-library/checkbox.component.mjs +13 -5
  21. package/esm2022/lib/widget-library/checkboxes.component.mjs +10 -3
  22. package/esm2022/lib/widget-library/file.component.mjs +5 -2
  23. package/esm2022/lib/widget-library/hidden.component.mjs +5 -2
  24. package/esm2022/lib/widget-library/input.component.mjs +4 -1
  25. package/esm2022/lib/widget-library/number.component.mjs +4 -1
  26. package/esm2022/lib/widget-library/one-of.component.mjs +88 -4
  27. package/esm2022/lib/widget-library/radios.component.mjs +6 -3
  28. package/esm2022/lib/widget-library/root.component.mjs +74 -1
  29. package/esm2022/lib/widget-library/select.component.mjs +92 -7
  30. package/esm2022/lib/widget-library/submit.component.mjs +3 -2
  31. package/esm2022/lib/widget-library/tabs.component.mjs +77 -29
  32. package/esm2022/lib/widget-library/textarea.component.mjs +5 -2
  33. package/esm2022/lib/widget-library/widget-library.module.mjs +15 -3
  34. package/fesm2022/ng-formworks-core.mjs +1317 -239
  35. package/fesm2022/ng-formworks-core.mjs.map +1 -1
  36. package/lib/json-schema-form.component.d.ts +10 -1
  37. package/lib/json-schema-form.service.d.ts +22 -4
  38. package/lib/shared/form-group.functions.d.ts +29 -1
  39. package/lib/shared/format-regex.constants.d.ts +2 -1
  40. package/lib/shared/index.d.ts +6 -6
  41. package/lib/shared/json-schema.functions.d.ts +22 -0
  42. package/lib/shared/json.validators.d.ts +2 -2
  43. package/lib/shared/layout.functions.d.ts +1 -1
  44. package/lib/shared/utility.functions.d.ts +15 -0
  45. package/lib/widget-library/button.component.d.ts +3 -2
  46. package/lib/widget-library/checkbox.component.d.ts +3 -2
  47. package/lib/widget-library/checkboxes.component.d.ts +3 -2
  48. package/lib/widget-library/file.component.d.ts +3 -2
  49. package/lib/widget-library/hidden.component.d.ts +3 -2
  50. package/lib/widget-library/index.d.ts +1 -1
  51. package/lib/widget-library/input.component.d.ts +3 -2
  52. package/lib/widget-library/number.component.d.ts +3 -2
  53. package/lib/widget-library/one-of.component.d.ts +4 -2
  54. package/lib/widget-library/radios.component.d.ts +3 -2
  55. package/lib/widget-library/root.component.d.ts +1 -0
  56. package/lib/widget-library/select.component.d.ts +5 -2
  57. package/lib/widget-library/textarea.component.d.ts +3 -2
  58. package/package.json +1 -1
@@ -1,16 +1,19 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Injectable, inject, input, viewChild, ViewContainerRef, Component, Directive, Input, ChangeDetectionStrategy, ViewChild, ElementRef, NgZone, signal, NgModule, Inject, forwardRef, ChangeDetectorRef, output } from '@angular/core';
4
+ import { Injectable, inject, input, viewChild, ViewContainerRef, Component, Directive, Input, ChangeDetectionStrategy, ViewChild, signal, ElementRef, NgZone, NgModule, Inject, forwardRef, ChangeDetectorRef, output } from '@angular/core';
5
5
  import * as i2 from '@angular/forms';
6
6
  import { UntypedFormControl, UntypedFormArray, UntypedFormGroup, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import addFormats from 'ajv-formats';
7
8
  import Ajv2019 from 'ajv/dist/2019';
8
9
  import jsonDraft6 from 'ajv/lib/refs/json-schema-draft-06.json';
9
10
  import jsonDraft7 from 'ajv/lib/refs/json-schema-draft-07.json';
10
11
  import cloneDeep from 'lodash/cloneDeep';
11
12
  import { from, Observable, forkJoin, Subject, BehaviorSubject, lastValueFrom } from 'rxjs';
13
+ import { some, isNil, isEmpty as isEmpty$1, isObject as isObject$1, pick, isEqual as isEqual$2 } from 'lodash';
12
14
  import isEqual$1 from 'lodash/isEqual';
13
15
  import { map, takeUntil } from 'rxjs/operators';
16
+ import omit from 'lodash/omit';
14
17
  import filter from 'lodash/filter';
15
18
  import map$1 from 'lodash/map';
16
19
  import _isArray from 'lodash/isArray';
@@ -61,13 +64,15 @@ const deValidationMessages = {
61
64
  case 'uuid':
62
65
  return 'Keine gültige UUID (z. B. "12345678-9ABC-DEF0-1234-56789ABCDEF0")';
63
66
  case 'color':
64
- return 'Kein gültiger Farbwert (z. B. "#FFFFFF" oder "rgb(255, 255, 255)")';
67
+ return 'Kein gültiger Farbwert (z. B. "#FFFFFF")';
65
68
  case 'json-pointer':
66
69
  return 'Kein gültiger JSON-Pointer (z. B. "/pointer/to/something")';
67
70
  case 'relative-json-pointer':
68
71
  return 'Kein gültiger relativer JSON-Pointer (z. B. "2/pointer/to/something")';
69
72
  case 'regex':
70
73
  return 'Kein gültiger regulärer Ausdruck (z. B. "(1-)?\\d{3}-\\d{3}-\\d{4}")';
74
+ case 'duration':
75
+ return "Muss eine gültige ISO 8601-Dauer sein (z. B. 'PT1H30M')";
71
76
  default:
72
77
  return 'Muss diesem Format entsprechen: ' + error.requiredFormat;
73
78
  }
@@ -121,13 +126,15 @@ const enValidationMessages = {
121
126
  case 'uuid':
122
127
  return 'Must be a uuid, like "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
123
128
  case 'color':
124
- return 'Must be a color, like "#FFFFFF" or "rgb(255, 255, 255)"';
129
+ return 'Must be a color, like "#FFFFFF"';
125
130
  case 'json-pointer':
126
131
  return 'Must be a JSON Pointer, like "/pointer/to/something"';
127
132
  case 'relative-json-pointer':
128
133
  return 'Must be a relative JSON Pointer, like "2/pointer/to/something"';
129
134
  case 'regex':
130
135
  return 'Must be a regular expression, like "(1-)?\\d{3}-\\d{3}-\\d{4}"';
136
+ case 'duration':
137
+ return "Must be a valid ISO 8601 duration (e.g., 'PT1H30M')";
131
138
  default:
132
139
  return 'Must be a correctly formatted ' + error.requiredFormat;
133
140
  }
@@ -179,13 +186,15 @@ const esValidationMessages = {
179
186
  case 'uuid':
180
187
  return 'Debe ser un UUID, ej "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
181
188
  case 'color':
182
- return 'Debe ser un color, ej "#FFFFFF" or "rgb(255, 255, 255)"';
189
+ return 'Debe ser un color, ej "#FFFFFF"';
183
190
  case 'json-pointer':
184
191
  return 'Debe ser un JSON Pointer, ej "/pointer/to/something"';
185
192
  case 'relative-json-pointer':
186
193
  return 'Debe ser un JSON Pointer relativo, ej "2/pointer/to/something"';
187
194
  case 'regex':
188
195
  return 'Debe ser una expresión regular, ej "(1-)?\\d{3}-\\d{3}-\\d{4}"';
196
+ case 'duration':
197
+ return "Debe ser una duración válida en formato ISO 8601 (p. ej., 'PT1H30M')";
189
198
  default:
190
199
  return 'Debe tener el formato correcto ' + error.requiredFormat;
191
200
  }
@@ -238,13 +247,15 @@ const frValidationMessages = {
238
247
  case 'uuid':
239
248
  return 'Doit être un UUID, tel que "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
240
249
  case 'color':
241
- return 'Doit être une couleur, tel que "#FFFFFF" or "rgb(255, 255, 255)"';
250
+ return 'Doit être une couleur, tel que "#FFFFFF"';
242
251
  case 'json-pointer':
243
252
  return 'Doit être un JSON Pointer, tel que "/pointer/to/something"';
244
253
  case 'relative-json-pointer':
245
254
  return 'Doit être un relative JSON Pointer, tel que "2/pointer/to/something"';
246
255
  case 'regex':
247
256
  return 'Doit être une expression régulière, tel que "(1-)?\\d{3}-\\d{3}-\\d{4}"';
257
+ case 'duration':
258
+ return "Doit être une durée valide au format ISO 8601 (par ex., 'PT1H30M')";
248
259
  default:
249
260
  return 'Doit être avoir le format correct: ' + error.requiredFormat;
250
261
  }
@@ -298,13 +309,15 @@ const itValidationMessages = {
298
309
  case 'uuid':
299
310
  return 'Deve essere un uuid, come "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
300
311
  case 'color':
301
- return 'Deve essere un colore, come "#FFFFFF" o "rgb(255, 255, 255)"';
312
+ return 'Deve essere un colore, come "#FFFFFF"';
302
313
  case 'json-pointer':
303
314
  return 'Deve essere un JSON Pointer, come "/pointer/to/something"';
304
315
  case 'relative-json-pointer':
305
316
  return 'Deve essere un JSON Pointer relativo, come "2/pointer/to/something"';
306
317
  case 'regex':
307
318
  return 'Deve essere una regular expression, come "(1-)?\\d{3}-\\d{3}-\\d{4}"';
319
+ case 'duration':
320
+ return "Deve essere una durata valida nel formato ISO 8601 (es. 'PT1H30M')";
308
321
  default:
309
322
  return 'Deve essere formattato correttamente ' + error.requiredFormat;
310
323
  }
@@ -358,13 +371,15 @@ const ptValidationMessages = {
358
371
  case 'uuid':
359
372
  return 'Tem que ser um uuid, por exemplo "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
360
373
  case 'color':
361
- return 'Tem que ser uma cor, por exemplo "#FFFFFF" ou "rgb(255, 255, 255)"';
374
+ return 'Tem que ser uma cor, por exemplo "#FFFFFF"';
362
375
  case 'json-pointer':
363
376
  return 'Tem que ser um JSON Pointer, por exemplo "/referencia/para/algo"';
364
377
  case 'relative-json-pointer':
365
378
  return 'Tem que ser um JSON Pointer relativo, por exemplo "2/referencia/para/algo"';
366
379
  case 'regex':
367
380
  return 'Tem que ser uma expressão regular, por exemplo "(1-)?\\d{3}-\\d{3}-\\d{4}"';
381
+ case 'duration':
382
+ return "Deve ser uma duração válida no formato ISO 8601 (ex.: 'PT1H30M')";
368
383
  default:
369
384
  return 'Tem que ser no formato: ' + error.requiredFormat;
370
385
  }
@@ -418,13 +433,15 @@ const zhValidationMessages = {
418
433
  case 'uuid':
419
434
  return '必须为 uuid, 比如 "12345678-9ABC-DEF0-1234-56789ABCDEF0"';
420
435
  case 'color':
421
- return '必须为颜色值, 比如 "#FFFFFF" 或者 "rgb(255, 255, 255)"';
436
+ return '必须为颜色值, 比如 "#FFFFFF"';
422
437
  case 'json-pointer':
423
438
  return '必须为 JSON Pointer, 比如 "/pointer/to/something"';
424
439
  case 'relative-json-pointer':
425
440
  return '必须为相对的 JSON Pointer, 比如 "2/pointer/to/something"';
426
441
  case 'regex':
427
442
  return '必须为正则表达式, 比如 "(1-)?\\d{3}-\\d{3}-\\d{4}"';
443
+ case 'duration':
444
+ return "必须是有效的 ISO 8601 持续时间(例如:'PT1H30M')";
428
445
  default:
429
446
  return '必须为格式正确的 ' + error.requiredFormat;
430
447
  }
@@ -1411,6 +1428,35 @@ function toTitleCase(input, forceWords) {
1411
1428
  }
1412
1429
  });
1413
1430
  }
1431
+ /**
1432
+ * Recursively checks if at least one property of the given object (including nested objects)
1433
+ * has a non-null and non-undefined value.
1434
+ *
1435
+ * @param obj - The object to check.
1436
+ * @returns `true` if at least one property has a non-null and non-undefined value, otherwise `false`.
1437
+ *
1438
+ * @example
1439
+ * const testObj = { a: null, b: { b1: null, b2: undefined } };
1440
+ * console.log(hasNonNullValue(testObj)); // Output: false
1441
+ *
1442
+ * const testObj2 = { a: 1, b: { b1: null, b2: undefined } };
1443
+ * console.log(hasNonNullValue(testObj2)); // Output: true
1444
+ */
1445
+ function hasNonNullValue(obj) {
1446
+ // If the object is null or not an object, return false immediately
1447
+ if (obj === null || typeof obj !== 'object') {
1448
+ return false;
1449
+ }
1450
+ // _.some checks if at least one element passes the given condition.
1451
+ return some(obj, (value) => {
1452
+ // If value is an object, recurse deeper into the object.
1453
+ if (isObject(value)) {
1454
+ return hasNonNullValue(value);
1455
+ }
1456
+ // Check if value is neither null nor undefined.
1457
+ return !isNil(value);
1458
+ });
1459
+ }
1414
1460
 
1415
1461
  class JsonPointer {
1416
1462
  /**
@@ -2475,6 +2521,7 @@ const jsonSchemaFormatTests = {
2475
2521
  // JSON-pointer: https://tools.ietf.org/html/rfc6901
2476
2522
  'json-pointer': /^(?:\/(?:[^~/]|~0|~1)*)*$|^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,
2477
2523
  'relative-json-pointer': /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/,
2524
+ 'duration': /^P(?!$)(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$/,
2478
2525
  'regex': function (str) {
2479
2526
  if (/[^\\]\\Z/.test(str)) {
2480
2527
  return false;
@@ -2771,7 +2818,7 @@ class JsonValidators {
2771
2818
  * This validator currently checks the following formsts:
2772
2819
  * date, time, date-time, email, hostname, ipv4, ipv6,
2773
2820
  * uri, uri-reference, uri-template, url, uuid, color,
2774
- * json-pointer, relative-json-pointer, regex
2821
+ * json-pointer, relative-json-pointer,duration, regex
2775
2822
  *
2776
2823
  * Fast format regular expressions copied from AJV:
2777
2824
  * https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js
@@ -2790,9 +2837,14 @@ class JsonValidators {
2790
2837
  let isValid;
2791
2838
  const currentValue = control.value;
2792
2839
  if (isString(currentValue)) {
2840
+ //TODO fix-Reg exp last index problem
2841
+ //see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test
2842
+ //before every call to .test it needs to be reset
2843
+ //use either formatTest.lastIndex = 0; // Reset the lastIndex before each test
2844
+ //or new RegExp(formatTest.source, formatTest.flags);
2793
2845
  const formatTest = jsonSchemaFormatTests[requiredFormat];
2794
2846
  if (typeof formatTest === 'object') {
2795
- isValid = formatTest.test(currentValue);
2847
+ isValid = new RegExp(formatTest.source, formatTest.flags).test(currentValue);
2796
2848
  }
2797
2849
  else if (typeof formatTest === 'function') {
2798
2850
  isValid = formatTest(currentValue);
@@ -4029,8 +4081,18 @@ function getInputType(schema, layoutNode = null) {
4029
4081
  if (hasOwn(schema, '$ref')) {
4030
4082
  return '$ref';
4031
4083
  }
4032
- if (isArray(schema.oneOf) || isArray(schema.anyOf)) {
4084
+ //if (isArray(schema.anyOf)) { return 'any-of'; }//treated as allOf
4085
+ if (isArray(schema.oneOf)) {
4033
4086
  return 'one-of';
4087
+ } //{ return 'tabarray'; }
4088
+ if (hasOwn(schema, 'if')) {
4089
+ return 'if';
4090
+ }
4091
+ if (hasOwn(schema, 'then')) {
4092
+ return 'then';
4093
+ }
4094
+ if (hasOwn(schema, 'else')) {
4095
+ return 'else';
4034
4096
  }
4035
4097
  console.error(`getInputType error: Unable to determine input type for ${schemaType}`);
4036
4098
  console.error('schema', schema);
@@ -4443,6 +4505,14 @@ function getSubSchema(schema, pointer, schemaRefLibrary = null, schemaRecursiveR
4443
4505
  return subSchema;
4444
4506
  }, true, pointer);
4445
4507
  }
4508
+ function omitKeys(objects, keysToOmit) {
4509
+ return objects.map((obj) => omit(obj, keysToOmit));
4510
+ }
4511
+ function combineAllOfITE(schema) {
4512
+ if (schema && schema.allOf) {
4513
+ const allITE = schema.allOf.map(item => { return item.if && { if: item.if }; });
4514
+ }
4515
+ }
4446
4516
  /**
4447
4517
  * 'combineAllOf' function
4448
4518
  *
@@ -4456,11 +4526,24 @@ function combineAllOf(schema) {
4456
4526
  if (!isObject(schema) || !isArray(schema.allOf)) {
4457
4527
  return schema;
4458
4528
  }
4459
- let mergedSchema = mergeSchemas(...schema.allOf);
4529
+ const allITE = schema.allOf.map(item => { return item.if && item; })
4530
+ .filter(item => !isEmpty$1(item));
4531
+ //adaped to accomodate ITE by merging all non ITE field
4532
+ //then readding the allOf key with only ITE
4533
+ let schemaITEOmitted = omitKeys(schema.allOf, ['if', 'then', 'else']);
4534
+ let mergedSchema = mergeSchemas(...schemaITEOmitted);
4535
+ //mergeSchemas(...schema.allOf);
4460
4536
  if (Object.keys(schema).length > 1) {
4461
4537
  const extraKeys = { ...schema };
4462
4538
  delete extraKeys.allOf;
4463
- mergedSchema = mergeSchemas(mergedSchema, extraKeys);
4539
+ //TODO Test-changed order to preserve originial order
4540
+ mergedSchema = mergeSchemas(extraKeys, mergedSchema);
4541
+ //mergeSchemas(mergedSchema, extraKeys);
4542
+ //need to put it back if ITE
4543
+ if (allITE && allITE.length > 0) {
4544
+ mergedSchema.allOf = mergedSchema.allOf || [];
4545
+ mergedSchema.allOf.push(...allITE);
4546
+ }
4464
4547
  }
4465
4548
  return mergedSchema;
4466
4549
  }
@@ -4486,6 +4569,65 @@ function fixRequiredArrayProperties(schema) {
4486
4569
  }
4487
4570
  return schema;
4488
4571
  }
4572
+ /**
4573
+ * 'convertJSONSchemaIfToCondition' function
4574
+ * converts something like
4575
+ * "if": {
4576
+ * "properties": {
4577
+ * "animal": {
4578
+ * "const": "Cat"
4579
+ * },
4580
+ * "habitat":{
4581
+ * "const": "City"
4582
+ * }
4583
+ * }
4584
+ * }
4585
+ * to "model.animal=='Cat' && habitat=='City" contion
4586
+ * @param schema:any
4587
+ * @param negate:boolean=false
4588
+ * @returns
4589
+
4590
+ */
4591
+ function convertJSONSchemaIfToCondition(schema, layoutNode, negate = false) {
4592
+ let conditionFun = "";
4593
+ let condition = {};
4594
+ let notOp = negate ? "!" : "";
4595
+ // expects "dataPointer" to be like "/a/b/c"
4596
+ //TODO-test
4597
+ //dataPointer can be something like /cities/-/name
4598
+ //must end up like model.cities[arrayIndices].name
4599
+ //also check can possibly be nested array like /cities/-/sites/-/siteName
4600
+ //in this case must probably end up like
4601
+ // /cities/arrayIndices[0]/sites/arrayIndices[1]/siteName
4602
+ //but it seems evaluatCondition support only one level for now
4603
+ //and uses arrayIndices as the last index only -check?
4604
+ let parentPath = layoutNode.dataPointer ? layoutNode.dataPointer
4605
+ .split("/")
4606
+ .slice(1, -1)
4607
+ .map((part, ind) => {
4608
+ let sep = ind == 0 ? "" : ".";
4609
+ let ret = part == "-" ? "[arrayIndices]" : sep + part;
4610
+ return ret;
4611
+ })
4612
+ .join("")
4613
+ : "";
4614
+ let modelPath = parentPath ? `model.${parentPath}` : "model";
4615
+ let checkPath = modelPath.split(".")
4616
+ .reduce((accumulator, currentPart, index) => {
4617
+ const currentExpression = index === 0 ? currentPart : `${accumulator}.${currentPart}`;
4618
+ return index === 0 ? currentExpression : `${accumulator} && ${currentExpression}`;
4619
+ }, '');
4620
+ if (schema.if) {
4621
+ Object.keys(schema.if.properties).forEach((ifProp, ind) => {
4622
+ let amper = ind > 0 ? "&" : "";
4623
+ //Note the model value is first converted to string and so is the condition
4624
+ //so that booleans and numbers can also be compared
4625
+ conditionFun += `${amper} ${checkPath} && ${modelPath}.${ifProp}+""=='${schema.if.properties[ifProp].const}'`;
4626
+ });
4627
+ }
4628
+ condition["functionBody"] = `return ${notOp}(${conditionFun})`;
4629
+ return condition;
4630
+ }
4489
4631
 
4490
4632
  function convertSchemaToDraft6(schema, options = {}) {
4491
4633
  let draft = options.draft || null;
@@ -4786,6 +4928,17 @@ function convertSchemaToDraft6(schema, options = {}) {
4786
4928
  return newSchema;
4787
4929
  }
4788
4930
 
4931
+ /**
4932
+ * path2ControlKey takes a datapointer path like /some/pointer/path
4933
+ * and returns something like $some$pointer$path
4934
+ * used mainly to convert paths so it can be used as keys in FormGroups
4935
+ * fot ITE scenarios
4936
+ * @param path
4937
+ * @returns string
4938
+ */
4939
+ function path2ControlKey(path) {
4940
+ return path.replace(/\//g, "$");
4941
+ }
4789
4942
  /**
4790
4943
  * FormGroup function library:
4791
4944
  *
@@ -4834,11 +4987,13 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4834
4987
  }
4835
4988
  // TODO: If nodeValue still not set, check layout for default value
4836
4989
  const schemaType = JsonPointer.get(schema, '/type');
4837
- const controlType = (hasOwn(schema, 'properties') || hasOwn(schema, 'additionalProperties')) &&
4838
- schemaType === 'object' ? 'FormGroup' :
4839
- (hasOwn(schema, 'items') || hasOwn(schema, 'additionalItems')) &&
4840
- schemaType === 'array' ? 'FormArray' :
4841
- !schemaType && hasOwn(schema, '$ref') ? '$ref' : 'FormControl';
4990
+ const isIfThenElse = hasOwn(schema, 'if') || hasOwn(schema, 'then') || hasOwn(schema, 'else');
4991
+ const controlType = isIfThenElse && !schemaType ? 'IfThenElse' :
4992
+ (hasOwn(schema, 'properties') || hasOwn(schema, 'additionalProperties')) &&
4993
+ schemaType === 'object' ? 'FormGroup' :
4994
+ (hasOwn(schema, 'items') || hasOwn(schema, 'additionalItems')) &&
4995
+ schemaType === 'array' ? 'FormArray' :
4996
+ !schemaType && hasOwn(schema, '$ref') ? '$ref' : 'FormControl';
4842
4997
  const shortDataPointer = removeRecursiveReferences(dataPointer, jsf.dataRecursiveRefMap, jsf.arrayMap);
4843
4998
  if (!jsf.dataMap.has(shortDataPointer)) {
4844
4999
  jsf.dataMap.set(shortDataPointer, new Map());
@@ -4877,11 +5032,175 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4877
5032
  propertyKeys
4878
5033
  .filter(key => hasOwn(schema.properties, key) ||
4879
5034
  hasOwn(schema, 'additionalProperties'))
4880
- .forEach(key => controls[key] = buildFormGroupTemplate(jsf, JsonPointer.get(nodeValue, [key]), setValues, schemaPointer + (hasOwn(schema.properties, key) ?
4881
- '/properties/' + key : '/additionalProperties'), dataPointer + '/' + key, templatePointer + '/controls/' + key));
5035
+ .forEach(key => {
5036
+ controls[key] = buildFormGroupTemplate(jsf, JsonPointer.get(nodeValue, [key]), setValues, schemaPointer + (hasOwn(schema.properties, key) ?
5037
+ '/properties/' + key : '/additionalProperties'), dataPointer + '/' + key, templatePointer + '/controls/' + key);
5038
+ //add the $<control> type to the root
5039
+ //so it can be flattened and acceses directly in the formgroup
5040
+ //by its full '$' path
5041
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
5042
+ if (controls[key].controls && controls[key].controls[`_${ofType}`]) {
5043
+ Object.keys(controls[key].controls[`_${ofType}`]).forEach($key => {
5044
+ controls[$key] = controls[key].controls[`_${ofType}`][$key];
5045
+ delete controls[key].controls[$key];
5046
+ });
5047
+ delete controls[key].controls[`_${ofType}`];
5048
+ }
5049
+ });
5050
+ });
5051
+ if (hasOwn(schema, "if")) {
5052
+ ["then", "else"].forEach(con => {
5053
+ if (hasOwn(schema, con)) {
5054
+ const keySchemaPointer = `/${con}`;
5055
+ let thenFGTemplate = buildFormGroupTemplate(jsf, nodeValue, false, //JsonPointer.get(nodeValue, keySchemaPointer), setValues,
5056
+ schemaPointer + keySchemaPointer, dataPointer, templatePointer + `/controls/${con}`);
5057
+ Object.assign(controls, thenFGTemplate.controls);
5058
+ }
5059
+ });
5060
+ }
5061
+ /* treat allOf the same as any of but need to add an extra
5062
+ condition for which anyOf item is to be rendered
5063
+ let allOfControls = {}
5064
+ let allOfAllowedKeys = ["allOf", "anyOf", "oneOf", "if", "then", "else", "type", "properties", "items"];
5065
+ if (hasOwn(schema, "allOf") && isArray(schema.allOf)) {
5066
+ schema.allOf.forEach((allOfItem, ind) => {
5067
+ let aoItemKeys = Object.keys(allOfItem);
5068
+ let foundKeys = allOfAllowedKeys.filter(value =>
5069
+ aoItemKeys.includes(value)
5070
+ );
5071
+ if (foundKeys && foundKeys.length > 0) {
5072
+ const keySchemaPointer = `/allOf/${ind}`;
5073
+ //console.log(`found:${keySchemaPointer}`);
5074
+ let allOfFGTemplate = buildFormGroupTemplate(
5075
+ jsf, JsonPointer.get(nodeValue, keySchemaPointer), setValues,
5076
+ schemaPointer + keySchemaPointer,
5077
+ dataPointer,
5078
+ templatePointer + '/controls/' + ind
5079
+ );
5080
+ if (allOfFGTemplate.controls) {
5081
+ Object.keys(allOfFGTemplate.controls).forEach(key => {
5082
+ let controlKey = allOfFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}/${key}`;
5083
+ controlKey = path2ControlKey(controlKey);
5084
+ controls[controlKey] = {
5085
+ key: key,
5086
+ schemaPointer: schemaPointer + keySchemaPointer,
5087
+ controls: allOfFGTemplate.controls[key]
5088
+ }
5089
+ controls[controlKey] = allOfFGTemplate.controls[key];
5090
+ controls[controlKey].key = key;
5091
+ controls[controlKey].schemaPointer = allOfFGTemplate.controls[key].schemaPointer || schemaPointer + keySchemaPointer;
5092
+
5093
+ controls[key] = allOfFGTemplate.controls[key];
5094
+ })
5095
+ }
5096
+ //add ui type items to controls
5097
+ if (allOfItem["type"] || allOfItem["properties"] || allOfItem["items"]) {
5098
+ allOfControls[ind] = allOfFGTemplate
5099
+ }
5100
+
5101
+ }
5102
+
5103
+ })
5104
+ controls["allOf"] = allOfControls;
5105
+ }
5106
+ */
5107
+ let ofAllowedKeys = ["allOf", "anyOf", "oneOf", "if", "then", "else", "type", "properties", "items"];
5108
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
5109
+ if (hasOwn(schema, ofType) && isArray(schema[ofType])) {
5110
+ schema[ofType].forEach((ofItem, ind) => {
5111
+ let aoItemKeys = Object.keys(ofItem);
5112
+ let foundKeys = ofAllowedKeys.filter(value => aoItemKeys.includes(value));
5113
+ if (foundKeys && foundKeys.length > 0) {
5114
+ const keySchemaPointer = `/${ofType}/${ind}`;
5115
+ //console.log(`found:${keySchemaPointer}`);
5116
+ let newNodeValue = JsonPointer.get(nodeValue, dataPointer);
5117
+ //JsonPointer.get(nodeValue, keySchemaPointer);
5118
+ if (ofType == "oneOf") {
5119
+ newNodeValue = nodeValue;
5120
+ }
5121
+ let allOfFGTemplate = buildFormGroupTemplate(jsf, newNodeValue, setValues, schemaPointer + keySchemaPointer, dataPointer, templatePointer + '/controls/' + ind);
5122
+ if (allOfFGTemplate.controls) {
5123
+ Object.keys(allOfFGTemplate.controls).forEach(key => {
5124
+ const l2SchemaPointer = hasOwn(schema, 'properties') ?
5125
+ '/properties/' + key : key;
5126
+ let controlKey = allOfFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}${l2SchemaPointer}`;
5127
+ controlKey = path2ControlKey(controlKey);
5128
+ /*
5129
+ controls[controlKey] = {
5130
+ key: key,
5131
+ schemaPointer: `${schemaPointer}${keySchemaPointer}/${key}`,//schemaPointer + keySchemaPointer,
5132
+ controls: allOfFGTemplate.controls[key]
5133
+ }
5134
+ */
5135
+ let controlItem = cloneDeep(allOfFGTemplate.controls[key]);
5136
+ controlItem.key = key;
5137
+ controlItem.schemaPointer = controlItem.schemaPointer || `${schemaPointer}${keySchemaPointer}${l2SchemaPointer}`;
5138
+ controls[controlKey] = controlItem;
5139
+ //need to test if value matches schema,
5140
+ //as the same oneOf item will be assigned to the same value
5141
+ //if key is a $oneOf key then it was inserted at the root of the controls
5142
+ //as form control name will be the full(escaped) path
5143
+ const pointerPath = key.startsWith('$oneOf') ? controlItem.schemaPointer : keySchemaPointer;
5144
+ let oneOfItemSchema = JsonPointer.get(jsf.schema, controlItem.schemaPointer);
5145
+ //JsonPointer.get(schema,pointerPath);
5146
+ let dPointer = controlItem.schemaPointer.replace(/(anyOf|allOf|oneOf|none)\/[\d]+\//g, '')
5147
+ .replace(/(if|then|else|properties)\//g, '');
5148
+ //JsonPointer.toDataPointer(controlItem.schemaPointer,jsf.schema);
5149
+ let dVal = JsonPointer.get(nodeValue, dPointer);
5150
+ let fkey = key;
5151
+ let oneOfItemValue = dVal;
5152
+ /*
5153
+ if(hasOwn(oneOfItemSchema,"if") && controlItem.schemaPointer
5154
+ && controlItem.schemaPointer.indexOf(keySchemaPointer)==0){
5155
+ let parts=controlItem.schemaPointer
5156
+ .split(keySchemaPointer).join('').split("/")
5157
+ let thenOrElse=parts[1];
5158
+ fkey=parts[parts.length-1];
5159
+ oneOfItemSchema=oneOfItemSchema[thenOrElse];
5160
+ }
5161
+
5162
+ if(oneOfItemSchema.properties && jsf.formValues===undefined){
5163
+ //check if no form data values were supplied
5164
+ //then set it to default otherwise to its nodevalue
5165
+ oneOfItemValue=oneOfItemSchema.default
5166
+ oneOfItemValue[fkey]=oneOfItemSchema.properties[fkey]?.default;
5167
+ }
5168
+ if(oneOfItemSchema.properties && jsf.formValues!=undefined){
5169
+ oneOfItemValue ={};
5170
+ //nodeValue||{};
5171
+ oneOfItemValue[fkey]=nodeValue&&nodeValue[fkey];
5172
+ }
5173
+ if(!oneOfItemSchema.properties && jsf.formValues==undefined){
5174
+ oneOfItemValue=oneOfItemSchema.default;
5175
+ }
5176
+ */
5177
+ if (hasOwn(controlItem, "value")) {
5178
+ if (!jsf.ajv.validate(oneOfItemSchema, oneOfItemValue)) {
5179
+ controlItem.value.value = null;
5180
+ }
5181
+ else {
5182
+ ///controlItem.value.value=oneOfItemValue[fkey];
5183
+ controlItem.value.value = oneOfItemSchema.properties ? oneOfItemValue[fkey] : oneOfItemValue;
5184
+ }
5185
+ }
5186
+ //controls[controlKey] = controlItem;
5187
+ //allOfFGTemplate.controls[key].schemaPointer ||`${schemaPointer}${keySchemaPointer}/${key}`;
5188
+ //allOfFGTemplate.controls[key].schemaPointer || schemaPointer + keySchemaPointer;
5189
+ ///////controls[key] = cloneDeep(allOfFGTemplate.controls[key]);
5190
+ //add schemacontrol to root
5191
+ //controls[controlKey]=controlItem
5192
+ controls[`_${ofType}`] = controls[`_${ofType}`] || {};
5193
+ controls[`_${ofType}`][controlKey] = controlItem;
5194
+ //allOfFGTemplate.controls[key];
5195
+ });
5196
+ }
5197
+ }
5198
+ });
5199
+ }
5200
+ });
4882
5201
  jsf.formOptions.fieldsRequired = setRequiredFields(schema, controls);
4883
5202
  }
4884
- return { controlType, controls, validators };
5203
+ return { controlType, controls, validators, schemaPointer };
4885
5204
  case 'FormArray':
4886
5205
  controls = [];
4887
5206
  const minItems = Math.max(schema.minItems || 0, nodeOptions.get('minItems') || 0);
@@ -4938,7 +5257,7 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4938
5257
  }
4939
5258
  }
4940
5259
  }
4941
- return { controlType, controls, validators };
5260
+ return { controlType, controls, validators, schemaPointer };
4942
5261
  case '$ref':
4943
5262
  const schemaRef = JsonPointer.compile(schema.$ref);
4944
5263
  const dataRef = JsonPointer.toDataPointer(schemaRef, schema);
@@ -4960,7 +5279,57 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4960
5279
  value: setValues && isPrimitive(nodeValue) ? nodeValue : null,
4961
5280
  disabled: nodeOptions.get('disabled') || false
4962
5281
  };
4963
- return { controlType, value, validators };
5282
+ return { controlType, value, validators, schemaPointer };
5283
+ //TODO may make an IFThenElse widget or integrate it with the section
5284
+ //widget
5285
+ case 'IfThenElse':
5286
+ controls = {};
5287
+ let conditionType;
5288
+ if (hasOwn(schema, "if")) {
5289
+ ["then", "else"].forEach(con => {
5290
+ if (hasOwn(schema, con)) {
5291
+ const keySchemaPointer = `/${con}`;
5292
+ let thenTFGTemplate = buildFormGroupTemplate(jsf, nodeValue, false, schemaPointer + keySchemaPointer, dataPointer, templatePointer + `/controls/${con}`);
5293
+ //NB same property can be in both then and else
5294
+ //so key must be the unique path to control
5295
+ //let ifItemSchema=JsonPointer.get(schema,keySchemaPointer);
5296
+ //let ifItemValue;
5297
+ Object.keys(thenTFGTemplate.controls).forEach(key => {
5298
+ let controlKey = thenTFGTemplate.controls[key].schemaPointer;
5299
+ ////let controlItem=cloneDeep(thenTFGTemplate.controls[key]);
5300
+ ////thenTFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}/${key}`;
5301
+ controlKey = path2ControlKey(controlKey);
5302
+ let cItem = Object.assign({}, thenTFGTemplate.controls[key]);
5303
+ ////cItem.schemaPointer = `${schemaPointer}${keySchemaPointer}/${key}`;
5304
+ /*
5305
+ if(ifItemSchema.properties && jsf.formValues===undefined){
5306
+ //check if no form data values were supplied
5307
+ //then set it to default otherwise to its nodevalue
5308
+ ifItemValue=ifItemSchema.default
5309
+ ifItemValue[key]=ifItemSchema.properties[key]?.default;
5310
+ }
5311
+ if(ifItemSchema.properties && jsf.formValues!=undefined){
5312
+ ifItemValue ={};
5313
+ //nodeValue||{};
5314
+ ifItemValue[key]=nodeValue&&nodeValue[key];
5315
+ }
5316
+ if(!ifItemSchema.properties && jsf.formValues==undefined){
5317
+ ifItemValue=ifItemSchema.default;
5318
+ }
5319
+ if(hasOwn(cItem,"value")){
5320
+ if(!jsf.ajv.validate(ifItemSchema,ifItemValue)){
5321
+ cItem.value.value=null;
5322
+ }else{
5323
+ cItem.value.value=ifItemValue[key];
5324
+ }
5325
+ }
5326
+ */
5327
+ controls[controlKey] = cItem;
5328
+ });
5329
+ }
5330
+ });
5331
+ }
5332
+ return { controlType, controls, validators, schemaPointer };
4964
5333
  default:
4965
5334
  return null;
4966
5335
  }
@@ -4992,7 +5361,23 @@ function buildFormGroup(template) {
4992
5361
  const groupControls = {};
4993
5362
  forEach(template.controls, (controls, key) => {
4994
5363
  const newControl = buildFormGroup(controls);
5364
+ //if (newControl) { groupControls[key] = newControl; }
4995
5365
  if (newControl) {
5366
+ /* experimental idea was to try to be able to switch
5367
+ conditional controls dynamically based on their schema pointer
5368
+ (not datapointer as that only maps to one control)
5369
+ Object.defineProperty(groupControls, key, {
5370
+ get: () => {
5371
+ //console.log(`Accessed control: ${key}`);
5372
+ //add switch logic here
5373
+ return ncontrol;
5374
+ },
5375
+ set:(value)=>{
5376
+ ncontrol=value
5377
+ },
5378
+ enumerable: true
5379
+ })
5380
+ */
4996
5381
  groupControls[key] = newControl;
4997
5382
  }
4998
5383
  });
@@ -5142,6 +5527,9 @@ function formatFormData(formData, dataMap, recursiveRefMap, arrayMap, returnEmpt
5142
5527
  }
5143
5528
  else if (typeof value !== 'object' || isDate(value) ||
5144
5529
  (value === null && returnEmptyFields)) {
5530
+ if (genericPointer.indexOf("/$") >= 0) {
5531
+ return formattedData;
5532
+ }
5145
5533
  console.error('formatFormData error: ' +
5146
5534
  `Schema type not found for form value at ${genericPointer}`);
5147
5535
  console.error('dataMap', dataMap);
@@ -5166,14 +5554,15 @@ function formatFormData(formData, dataMap, recursiveRefMap, arrayMap, returnEmpt
5166
5554
  * // {Pointer} dataPointer - JSON Pointer (string or array)
5167
5555
  * // {boolean = false} returnGroup - If true, return group containing control
5168
5556
  * // {group} - Located value (or null, if no control found)
5557
+ * // {string} schemaPointer - string used for conditional controls coming from schema if/then/else
5169
5558
  */
5170
- function getControl(formGroup, dataPointer, returnGroup = false) {
5559
+ function getControl(formGroup, dataPointer, returnGroup = false, schemaPointer) {
5171
5560
  if (!isObject(formGroup) || !JsonPointer.isJsonPointer(dataPointer)) {
5172
5561
  if (!JsonPointer.isJsonPointer(dataPointer)) {
5173
5562
  // If dataPointer input is not a valid JSON pointer, check to
5174
5563
  // see if it is instead a valid object path, using dot notaion
5175
5564
  if (typeof dataPointer === 'string') {
5176
- const formControl = formGroup.get(dataPointer);
5565
+ const formControl = formGroup.get(path2ControlKey(schemaPointer || "")) || formGroup.get(dataPointer);
5177
5566
  if (formControl) {
5178
5567
  return formControl;
5179
5568
  }
@@ -5193,7 +5582,7 @@ function getControl(formGroup, dataPointer, returnGroup = false) {
5193
5582
  // try using formGroup.get() to return the control
5194
5583
  if (typeof formGroup.get === 'function' &&
5195
5584
  dataPointerArray.every(key => key.indexOf('.') === -1)) {
5196
- const formControl = formGroup.get(dataPointerArray.join('.'));
5585
+ const formControl = formGroup.get(path2ControlKey(schemaPointer || "")) || formGroup.get(dataPointerArray.join('.'));
5197
5586
  if (formControl) {
5198
5587
  return formControl;
5199
5588
  }
@@ -5212,6 +5601,9 @@ function getControl(formGroup, dataPointer, returnGroup = false) {
5212
5601
  else if (hasOwn(subGroup, key)) {
5213
5602
  subGroup = subGroup[key];
5214
5603
  }
5604
+ else if (schemaPointer && hasOwn(subGroup, path2ControlKey(schemaPointer))) {
5605
+ subGroup = subGroup[path2ControlKey(schemaPointer)];
5606
+ }
5215
5607
  else {
5216
5608
  console.error(`getControl error: Unable to find "${key}" item in FormGroup.`);
5217
5609
  console.error(dataPointer);
@@ -5221,6 +5613,68 @@ function getControl(formGroup, dataPointer, returnGroup = false) {
5221
5613
  }
5222
5614
  return subGroup;
5223
5615
  }
5616
+ /**
5617
+ * 'setControl' function
5618
+ *
5619
+ * Uses a JSON Pointer for a data object to retrieve a control from
5620
+ * an Angular formGroup or formGroup template. (Note: though a formGroup
5621
+ * template is much simpler, its basic structure is idential to a formGroup).
5622
+ *
5623
+ * If the optional third parameter 'returnGroup' is set to TRUE, the group
5624
+ * containing the control is returned, rather than the control itself.
5625
+ *
5626
+ * // {FormGroup} formGroup - Angular FormGroup to get value from
5627
+ * // {Pointer} dataPointer - JSON Pointer (string or array)
5628
+ * // {AbstractControl} control - control used to replace existing or add
5629
+ * // {targetKey} - optional string used as the new key-not implemented as yet
5630
+ */
5631
+ function setControl(formGroup, dataPointer, control, targetKey) {
5632
+ let dataPointerArray = JsonPointer.parse(dataPointer);
5633
+ // If formGroup input is a real formGroup (not a formGroup template)
5634
+ // try using formGroup.get() to return the control
5635
+ /*
5636
+ if (typeof formGroup.get === 'function' &&
5637
+ dataPointerArray.every(key => key.indexOf('.') === -1)
5638
+ ) {
5639
+ formGroup.setControl(dataPointerArray.join('.'), control);
5640
+ return;
5641
+ }
5642
+ */
5643
+ let currentGroup = formGroup;
5644
+ for (let i = 0; i < dataPointerArray.length - 1; i++) {
5645
+ // Navigate down the form structure to find the correct nested FormGroup
5646
+ currentGroup = currentGroup.get(dataPointerArray[i]);
5647
+ // If it's not a FormGroup, we throw an error since we can't set a control in a non-group.
5648
+ if (!(typeof currentGroup.setControl === 'function')) {
5649
+ throw new Error(`Path '${dataPointerArray[i]}' is not a valid FormGroup or FormArray.`);
5650
+ }
5651
+ }
5652
+ // Now we are at the parent FormGroup, set the control at the last part of the path
5653
+ const lastPart = dataPointerArray[dataPointerArray.length - 1];
5654
+ // Set the control at the final path (like 'name' inside 'state')
5655
+ currentGroup.setControl(lastPart, control);
5656
+ // If formGroup input is a formGroup template,
5657
+ // or formGroup.get() failed to return the control,
5658
+ // search the formGroup object for dataPointer's control
5659
+ //TODO needs to be adapted to setControl
5660
+ /*
5661
+ let subGroup = formGroup;
5662
+ for (const key of dataPointerArray) {
5663
+ if (hasOwn(subGroup, 'controls')) { subGroup = subGroup.controls; }
5664
+ if (isArray(subGroup) && (key === '-')) {
5665
+ subGroup = subGroup[subGroup.length - 1];
5666
+ } else if (hasOwn(subGroup, key)) {
5667
+ subGroup = subGroup[key];
5668
+ } else {
5669
+ console.error(`getControl error: Unable to find "${key}" item in FormGroup.`);
5670
+ console.error(dataPointer);
5671
+ console.error(formGroup);
5672
+ return;
5673
+ }
5674
+ }
5675
+ return subGroup;
5676
+ */
5677
+ }
5224
5678
 
5225
5679
  /**
5226
5680
  * Layout function library:
@@ -5390,7 +5844,6 @@ function buildLayout_original(jsf, widgetLibrary) {
5390
5844
  schemaPointer = JsonPointer.toSchemaPointer(shortDataPointer, jsf.schema);
5391
5845
  nodeDataMap.set('schemaPointer', schemaPointer);
5392
5846
  }
5393
- nodeDataMap.set('disabled', !!newNode.options.disabled);
5394
5847
  nodeSchema = JsonPointer.get(jsf.schema, schemaPointer);
5395
5848
  if (nodeSchema) {
5396
5849
  if (!hasOwn(newNode, 'type')) {
@@ -5411,6 +5864,7 @@ function buildLayout_original(jsf, widgetLibrary) {
5411
5864
  newNode.dataType =
5412
5865
  nodeSchema.type || (hasOwn(nodeSchema, '$ref') ? '$ref' : null);
5413
5866
  updateInputOptions(newNode, nodeSchema, jsf);
5867
+ nodeDataMap.set('disabled', !!newNode.options.disabled);
5414
5868
  // Present checkboxes as single control, rather than array
5415
5869
  if (newNode.type === 'checkboxes' && hasOwn(nodeSchema, 'items')) {
5416
5870
  updateInputOptions(newNode, nodeSchema.items, jsf);
@@ -5837,16 +6291,50 @@ function fixNestedArrayLayout(options) {
5837
6291
  * // { string = '' } dataPointerPrefix -
5838
6292
  * //
5839
6293
  */
5840
- function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPointer = '', dataPointer = '', arrayItem = false, arrayItemType = null, removable = null, forRefLibrary = false, dataPointerPrefix = '') {
5841
- const schema = JsonPointer.get(jsf.schema, schemaPointer);
6294
+ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPointer = '', dataPointer = '', arrayItem = false, arrayItemType = null, removable = null, forRefLibrary = false, dataPointerPrefix = '', jsonSchema) {
6295
+ function applyITEConditions(builtLayout, schPointer, keySchemaPointer, negateClause, parentLayout) {
6296
+ if (builtLayout) {
6297
+ if (parentLayout && parentLayout.isITEItem && parentLayout.options.condition) {
6298
+ return;
6299
+ }
6300
+ if (isArray(builtLayout)) {
6301
+ builtLayout.forEach(item => {
6302
+ item.isITEItem = true;
6303
+ item.options.condition = convertJSONSchemaIfToCondition(schema, item, negateClause);
6304
+ applyITEConditions(item, schPointer, keySchemaPointer, negateClause, builtLayout);
6305
+ //item.schemaPointer = schPointer + keySchemaPointer + item.dataPointer;
6306
+ //item.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6307
+ //newSection.push(item);
6308
+ });
6309
+ }
6310
+ else if (hasOwn(builtLayout, "items")) {
6311
+ applyITEConditions(builtLayout.items, schPointer, keySchemaPointer, negateClause, builtLayout);
6312
+ // builtLayout.items.forEach(item => {
6313
+ // item.isITEItem=true;
6314
+ // item.options.condition = convertJSONSchemaIfToCondition(schema,item, negateClause);
6315
+ // applyITEConditions(item,schPointer,keySchemaPointer,negateClause)
6316
+ // });
6317
+ }
6318
+ else {
6319
+ builtLayout.isITEItem = true;
6320
+ //builtLayout.schemaPointer = `${schPointer}${keySchemaPointer}/${builtLayout.name}`;
6321
+ builtLayout.options.condition = convertJSONSchemaIfToCondition(schema, builtLayout, negateClause);
6322
+ //newSection.push(builtLayout)
6323
+ }
6324
+ }
6325
+ }
6326
+ const jsSchema = jsonSchema || jsf.schema;
6327
+ const schema = JsonPointer.get(jsSchema, schemaPointer);
6328
+ //JsonPointer.get(jsf.schema, schemaPointer);
5842
6329
  if (!hasOwn(schema, 'type') && !hasOwn(schema, '$ref') &&
5843
- !hasOwn(schema, 'x-schema-form')) {
6330
+ !hasOwn(schema, 'x-schema-form')
6331
+ && !hasOwn(schema, 'if') && !hasOwn(schema, 'then') && !hasOwn(schema, 'else')) {
5844
6332
  return null;
5845
6333
  }
5846
6334
  const newNodeType = getInputType(schema);
5847
6335
  if (!isDefined(nodeValue) && (jsf.formOptions.setSchemaDefaults === true ||
5848
6336
  (jsf.formOptions.setSchemaDefaults === 'auto' && isEmpty(jsf.formValues)))) {
5849
- nodeValue = JsonPointer.get(jsf.schema, schemaPointer + '/default');
6337
+ nodeValue = JsonPointer.get(jsSchema, schemaPointer + '/default');
5850
6338
  }
5851
6339
  let newNode = {
5852
6340
  _id: forRefLibrary ? null : uniqueId(),
@@ -5854,7 +6342,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5854
6342
  dataPointer: JsonPointer.toGenericPointer(dataPointer, jsf.arrayMap),
5855
6343
  dataType: schema.type || (hasOwn(schema, '$ref') ? '$ref' : null),
5856
6344
  options: {},
5857
- required: isInputRequired(jsf.schema, schemaPointer),
6345
+ required: isInputRequired(jsSchema, schemaPointer),
5858
6346
  type: newNodeType,
5859
6347
  widget: widgetLibrary.getWidget(newNodeType),
5860
6348
  };
@@ -5872,6 +6360,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5872
6360
  if (!jsf.dataMap.has(shortDataPointer)) {
5873
6361
  jsf.dataMap.set(shortDataPointer, new Map());
5874
6362
  }
6363
+ updateInputOptions(newNode, schema, jsf);
5875
6364
  const nodeDataMap = jsf.dataMap.get(shortDataPointer);
5876
6365
  if (!nodeDataMap.has('inputType')) {
5877
6366
  nodeDataMap.set('schemaPointer', schemaPointer);
@@ -5879,7 +6368,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5879
6368
  nodeDataMap.set('widget', newNode.widget);
5880
6369
  nodeDataMap.set('disabled', !!newNode.options.disabled);
5881
6370
  }
5882
- updateInputOptions(newNode, schema, jsf);
6371
+ //updateInputOptions(newNode, schema, jsf);
5883
6372
  if (!newNode.options.title && newNode.name && !/^\d+$/.test(newNode.name)) {
5884
6373
  newNode.options.title = fixTitle(newNode.name);
5885
6374
  }
@@ -5907,6 +6396,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5907
6396
  '/properties/' + key : '/additionalProperties';
5908
6397
  const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, isObject(nodeValue) ? nodeValue[key] : null, schemaPointer + keySchemaPointer, dataPointer + '/' + key, false, null, null, forRefLibrary, dataPointerPrefix);
5909
6398
  if (innerItem) {
6399
+ innerItem.schemaPointer = schemaPointer + keySchemaPointer;
5910
6400
  if (isInputRequired(schema, '/' + key)) {
5911
6401
  innerItem.options.required = true;
5912
6402
  jsf.fieldsRequired = true;
@@ -5914,6 +6404,102 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5914
6404
  newSection.push(innerItem);
5915
6405
  }
5916
6406
  });
6407
+ //treat allOf the same as any of but need to add an extra
6408
+ //condition for which anyOf item is to be rendered
6409
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
6410
+ if (hasOwn(schema, ofType) && isArray(schema[ofType])) {
6411
+ let outerOneOfItem;
6412
+ if (ofType == "oneOf") {
6413
+ outerOneOfItem = buildLayoutFromSchema(jsf, widgetLibrary, schema.oneOf, //{type:"tabarray",items:schema.oneOf},
6414
+ "/", //schemaPointer + `/${ofType}`,
6415
+ dataPointer, false, null, null, forRefLibrary, dataPointerPrefix,
6416
+ //{type:"tabarray",items:schema.oneOf,oneOf:schema.oneOf}
6417
+ { type: "one-of", items: schema.oneOf, oneOf: schema.oneOf });
6418
+ //outerItem.items=cloneDeep(newSection);
6419
+ //newSection.length=0;
6420
+ newSection.push(outerOneOfItem);
6421
+ }
6422
+ schema[ofType].forEach((ofItem, ind) => {
6423
+ const keySchemaPointer = `/${ofType}/${ind}`;
6424
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, ofItem, schemaPointer + keySchemaPointer, dataPointer, false, null, null, ofType == "oneOf" /*forRefLibrary*/, dataPointerPrefix);
6425
+ if (innerItem) {
6426
+ //newSection.push(innerItem);
6427
+ if (innerItem.items) {
6428
+ innerItem.items.forEach(innerItemLevel2 => {
6429
+ const l2SchemaPointer = hasOwn(ofItem, 'properties') ?
6430
+ '/properties/' + innerItemLevel2.name : innerItemLevel2.name;
6431
+ //innerItemLevel2.oneOfPointer = schemaPointer + keySchemaPointer + l2SchemaPointer;
6432
+ // innerItemLevel2.schemaPointer=innerItemLevel2.schemaPointer;
6433
+ innerItemLevel2.oneOfPointer = innerItemLevel2.schemaPointer;
6434
+ });
6435
+ }
6436
+ //TODO review-will never reach here if forRefLibrary==true
6437
+ if (isArray(innerItem)) {
6438
+ let outerOneOfItemTpl = cloneDeep(newNode);
6439
+ outerOneOfItemTpl;
6440
+ innerItem.forEach(item => {
6441
+ const l2SchemaPointer = hasOwn(ofItem, 'properties') ?
6442
+ '/properties/' + item.name : item.name;
6443
+ if (outerOneOfItem) {
6444
+ ////item.oneOfPointer = schemaPointer + keySchemaPointer + l2SchemaPointer;
6445
+ //schemaPointer + keySchemaPointer + item.dataPointer;
6446
+ ////item.schemaPointer=item.oneOfPointer;
6447
+ /*
6448
+ outerOneOfItem.items=outerOneOfItem.items||[];
6449
+ outerOneOfItem.items.push(item);
6450
+ */
6451
+ outerOneOfItemTpl.items = outerOneOfItemTpl.items || [];
6452
+ outerOneOfItemTpl.items.push(item);
6453
+ }
6454
+ else {
6455
+ newSection.push(item);
6456
+ }
6457
+ });
6458
+ if (outerOneOfItem) {
6459
+ outerOneOfItem.items = outerOneOfItem.items || [];
6460
+ outerOneOfItem.items.push(outerOneOfItemTpl);
6461
+ }
6462
+ //TODO test-might not work for more than 2 levels of nesting
6463
+ }
6464
+ else {
6465
+ if (outerOneOfItem) {
6466
+ innerItem.oneOfPointer = schemaPointer + keySchemaPointer; // + innerItem.dataPointer;
6467
+ ////innerItem.schemaPointer=innerItem.oneOfPointer;
6468
+ outerOneOfItem.items = outerOneOfItem.items || [];
6469
+ outerOneOfItem.items.push(innerItem);
6470
+ }
6471
+ else {
6472
+ newSection.push(innerItem);
6473
+ }
6474
+ }
6475
+ }
6476
+ });
6477
+ }
6478
+ });
6479
+ if (hasOwn(schema, "if")) {
6480
+ ["then", "else"].forEach(con => {
6481
+ if (hasOwn(schema, con)) {
6482
+ const keySchemaPointer = `/${con}`;
6483
+ const negateClause = con == "else";
6484
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, nodeValue.then, schemaPointer + keySchemaPointer, dataPointer, false, null, null, forRefLibrary, dataPointerPrefix);
6485
+ if (innerItem) {
6486
+ applyITEConditions(innerItem, schemaPointer, keySchemaPointer, negateClause);
6487
+ if (isArray(innerItem)) {
6488
+ innerItem.forEach(item => {
6489
+ //item.schemaPointer = schemaPointer + keySchemaPointer + item.dataPointer;
6490
+ //item.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6491
+ newSection.push(item);
6492
+ });
6493
+ }
6494
+ else {
6495
+ //innerItem.schemaPointer = schemaPointer + keySchemaPointer + innerItem.dataPointer;
6496
+ //innerItem.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6497
+ newSection.push(innerItem);
6498
+ }
6499
+ }
6500
+ }
6501
+ });
6502
+ }
5917
6503
  if (dataPointer === '' && !forRefLibrary) {
5918
6504
  newNode = newSection;
5919
6505
  }
@@ -5930,7 +6516,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5930
6516
  newNode.items = [];
5931
6517
  newNode.options.maxItems = Math.min(schema.maxItems || 1000, newNode.options.maxItems || 1000);
5932
6518
  newNode.options.minItems = Math.max(schema.minItems || 0, newNode.options.minItems || 0);
5933
- if (!newNode.options.minItems && isInputRequired(jsf.schema, schemaPointer)) {
6519
+ if (!newNode.options.minItems && isInputRequired(jsSchema, schemaPointer)) {
5934
6520
  newNode.options.minItems = 1;
5935
6521
  }
5936
6522
  if (!hasOwn(newNode.options, 'listItems')) {
@@ -6063,7 +6649,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6063
6649
  }
6064
6650
  else if (newNode.dataType === '$ref') {
6065
6651
  const schemaRef = JsonPointer.compile(schema.$ref);
6066
- const dataRef = JsonPointer.toDataPointer(schemaRef, jsf.schema);
6652
+ const dataRef = JsonPointer.toDataPointer(schemaRef, jsSchema);
6067
6653
  let buttonText = '';
6068
6654
  // Get newNode title
6069
6655
  if (newNode.options.add) {
@@ -6075,7 +6661,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6075
6661
  // If newNode doesn't have a title, look for title of parent array item
6076
6662
  }
6077
6663
  else {
6078
- const parentSchema = JsonPointer.get(jsf.schema, schemaPointer, 0, -1);
6664
+ const parentSchema = JsonPointer.get(jsSchema, schemaPointer, 0, -1);
6079
6665
  if (hasOwn(parentSchema, 'title')) {
6080
6666
  buttonText = 'Add to ' + parentSchema.title;
6081
6667
  }
@@ -6093,9 +6679,9 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6093
6679
  removable: false,
6094
6680
  title: buttonText,
6095
6681
  });
6096
- if (isNumber(JsonPointer.get(jsf.schema, schemaPointer, 0, -1).maxItems)) {
6682
+ if (isNumber(JsonPointer.get(jsSchema, schemaPointer, 0, -1).maxItems)) {
6097
6683
  newNode.options.maxItems =
6098
- JsonPointer.get(jsf.schema, schemaPointer, 0, -1).maxItems;
6684
+ JsonPointer.get(jsSchema, schemaPointer, 0, -1).maxItems;
6099
6685
  }
6100
6686
  // Add layout template to layoutRefLibrary
6101
6687
  if (dataRef.length) {
@@ -6116,6 +6702,32 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6116
6702
  }
6117
6703
  }
6118
6704
  }
6705
+ else if (newNode.type === 'if') {
6706
+ const newSection = [];
6707
+ ["then", "else"].forEach(con => {
6708
+ if (hasOwn(schema, con)) {
6709
+ const keySchemaPointer = `/${con}`;
6710
+ const negateClause = con == "else";
6711
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, nodeValue.then, schemaPointer + keySchemaPointer, dataPointer, false, null, null, forRefLibrary, dataPointerPrefix);
6712
+ if (innerItem) {
6713
+ applyITEConditions(innerItem, schemaPointer, keySchemaPointer, negateClause);
6714
+ if (isArray(innerItem)) {
6715
+ innerItem.forEach(item => {
6716
+ //item.schemaPointer = schemaPointer + keySchemaPointer + item.dataPointer;
6717
+ //item.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6718
+ newSection.push(item);
6719
+ newNode = newSection;
6720
+ });
6721
+ }
6722
+ else {
6723
+ //innerItem.schemaPointer = schemaPointer + keySchemaPointer + innerItem.dataPointer;
6724
+ //innerItem.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6725
+ newNode = innerItem;
6726
+ }
6727
+ }
6728
+ }
6729
+ });
6730
+ }
6119
6731
  return newNode;
6120
6732
  }
6121
6733
  /**
@@ -6382,6 +6994,32 @@ class JsonSchemaFormService {
6382
6994
  setSortableOptions(value) {
6383
6995
  this.sortableOptionsSubject.next(value); // Update the sortable options value
6384
6996
  }
6997
+ createAjvInstance(ajvOptions) {
6998
+ let ajvInstance = new Ajv2019(ajvOptions);
6999
+ ajvInstance.addMetaSchema(jsonDraft6);
7000
+ ajvInstance.addMetaSchema(jsonDraft7);
7001
+ addFormats(ajvInstance);
7002
+ return ajvInstance;
7003
+ }
7004
+ createAndRegisterAjvInstance(ajvOptions, name) {
7005
+ const intanceName = name || `ajv_${Date.now()}`;
7006
+ if (this.ajvRegistry[intanceName]) {
7007
+ throw new Error(`ajv instance with name:'${intanceName}' has already been registered`);
7008
+ }
7009
+ const ajvInstance = this.createAjvInstance(ajvOptions);
7010
+ this.ajvRegistry[intanceName] = {
7011
+ name: intanceName,
7012
+ ajvInstance: ajvInstance,
7013
+ ajvValidator: null
7014
+ };
7015
+ return this.ajvRegistry[intanceName];
7016
+ }
7017
+ getAjvInstance(name = 'default') {
7018
+ return this.ajvRegistry[name].ajvInstance;
7019
+ }
7020
+ getAjvValidator(name = 'default') {
7021
+ return this.ajvRegistry[name]?.ajvValidator;
7022
+ }
6385
7023
  constructor() {
6386
7024
  this.JsonFormCompatibility = false;
6387
7025
  this.ReactJsonSchemaFormCompatibility = false;
@@ -6389,10 +7027,11 @@ class JsonSchemaFormService {
6389
7027
  this.tpldata = {};
6390
7028
  this.ajvOptions = {
6391
7029
  allErrors: true,
6392
- validateFormats: false,
7030
+ //validateFormats:false,
6393
7031
  strict: false
6394
7032
  };
6395
7033
  this.ajv = new Ajv2019(this.ajvOptions); // AJV: Another JSON Schema Validator
7034
+ //Being replaced by getAjvValidator()
6396
7035
  this.validateFormData = null; // Compiled AJV function to validate active form's schema
6397
7036
  this.formValues = {}; // Internal form data (may not have correct types)
6398
7037
  this.data = {}; // Output form data (formValues, formatted with correct data types)
@@ -6466,6 +7105,7 @@ class JsonSchemaFormService {
6466
7105
  validationMessages: {} // set by setLanguage()
6467
7106
  }
6468
7107
  };
7108
+ //TODO-review,may not be needed as sortablejs replaces dnd
6469
7109
  //this has been added to enable or disable the dragabble state of a component
6470
7110
  //using the OrderableDirective, mainly when an <input type="range">
6471
7111
  //elements are present, as the draggable attribute makes it difficult to
@@ -6478,9 +7118,19 @@ class JsonSchemaFormService {
6478
7118
  //nxt-sortablejs and sortablejs
6479
7119
  this.sortableOptionsSubject = new BehaviorSubject({ disabled: false }); // Default value true
6480
7120
  this.sortableOptions$ = this.sortableOptionsSubject.asObservable();
7121
+ this.ajvRegistry = {};
6481
7122
  this.setLanguage(this.language);
6482
7123
  this.ajv.addMetaSchema(jsonDraft6);
6483
7124
  this.ajv.addMetaSchema(jsonDraft7);
7125
+ addFormats(this.ajv);
7126
+ this.ajvRegistry['default'] = { name: 'default', ajvInstance: this.ajv, ajvValidator: null };
7127
+ // Add custom 'duration' format using a regex
7128
+ /*
7129
+ this.ajv.addFormat("duration", {
7130
+ type: "string",
7131
+ validate: (duration) => /^P(?!$)(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$/.test(duration)
7132
+ });
7133
+ */
6484
7134
  }
6485
7135
  ngOnDestroy() {
6486
7136
  this.fcValueChangesSubs?.unsubscribe();
@@ -6519,7 +7169,7 @@ class JsonSchemaFormService {
6519
7169
  this.ReactJsonSchemaFormCompatibility = false;
6520
7170
  this.AngularSchemaFormCompatibility = false;
6521
7171
  this.tpldata = {};
6522
- this.validateFormData = null;
7172
+ this.validateFormData = null; //Being replaced by getAjvValidator()
6523
7173
  this.formValues = {};
6524
7174
  this.schema = {};
6525
7175
  this.layout = [];
@@ -6538,6 +7188,8 @@ class JsonSchemaFormService {
6538
7188
  this.schemaRefLibrary = {};
6539
7189
  this.templateRefLibrary = {};
6540
7190
  this.formOptions = cloneDeep(this.defaultFormOptions);
7191
+ this.ajvRegistry = {};
7192
+ this.ajvRegistry['default'] = { name: 'default', ajvInstance: this.ajv, ajvValidator: null };
6541
7193
  }
6542
7194
  /**
6543
7195
  * 'buildRemoteError' function
@@ -6569,10 +7221,10 @@ class JsonSchemaFormService {
6569
7221
  }
6570
7222
  });
6571
7223
  }
6572
- validateData(newValue, updateSubscriptions = true) {
7224
+ validateData(newValue, updateSubscriptions = true, ajvInstanceName = 'default') {
6573
7225
  // Format raw form data to correct data types
6574
7226
  this.data = formatFormData(newValue, this.dataMap, this.dataRecursiveRefMap, this.arrayMap, this.formOptions.returnEmptyFields);
6575
- this.isValid = this.validateFormData(this.data);
7227
+ this.isValid = this.getAjvValidator(ajvInstanceName)(this.data);
6576
7228
  this.validData = this.isValid ? this.data : null;
6577
7229
  const compileErrors = (errors) => {
6578
7230
  const compiledErrors = {};
@@ -6587,8 +7239,9 @@ class JsonSchemaFormService {
6587
7239
  });
6588
7240
  return compiledErrors;
6589
7241
  };
6590
- this.ajvErrors = this.validateFormData.errors;
6591
- this.validationErrors = compileErrors(this.validateFormData.errors);
7242
+ //TODO:store avjErrors per ajvInstance in registry
7243
+ this.ajvErrors = this.getAjvValidator(ajvInstanceName).errors;
7244
+ this.validationErrors = compileErrors(this.ajvErrors);
6592
7245
  if (updateSubscriptions) {
6593
7246
  this.dataChanges.next(this.data);
6594
7247
  this.isValidChanges.next(this.isValid);
@@ -6598,16 +7251,16 @@ class JsonSchemaFormService {
6598
7251
  buildFormGroupTemplate(formValues = null, setValues = true) {
6599
7252
  this.formGroupTemplate = buildFormGroupTemplate(this, formValues, setValues);
6600
7253
  }
6601
- buildFormGroup() {
7254
+ buildFormGroup(ajvInstanceName) {
6602
7255
  this.formGroup = buildFormGroup(this.formGroupTemplate);
6603
7256
  if (this.formGroup) {
6604
- this.compileAjvSchema();
6605
- this.validateData(this.formGroup.value);
7257
+ this.compileAjvSchema(ajvInstanceName);
7258
+ this.validateData(this.formGroup.value, true, ajvInstanceName);
6606
7259
  // Set up observables to emit data and validation info when form data changes
6607
7260
  if (this.formValueSubscription) {
6608
7261
  this.formValueSubscription.unsubscribe();
6609
7262
  }
6610
- this.formValueSubscription = this.formGroup.valueChanges.subscribe(formValue => this.validateData(formValue));
7263
+ this.formValueSubscription = this.formGroup.valueChanges.subscribe(formValue => this.validateData(formValue, true, ajvInstanceName));
6611
7264
  }
6612
7265
  }
6613
7266
  buildLayout(widgetLibrary) {
@@ -6636,15 +7289,17 @@ class JsonSchemaFormService {
6636
7289
  });
6637
7290
  }
6638
7291
  }
6639
- compileAjvSchema() {
6640
- if (!this.validateFormData) {
7292
+ compileAjvSchema(ajvInstanceName = 'default') {
7293
+ let ajvValidator = this.getAjvValidator(ajvInstanceName);
7294
+ if (!ajvValidator) {
6641
7295
  // if 'ui:order' exists in properties, move it to root before compiling with ajv
6642
7296
  if (Array.isArray(this.schema.properties['ui:order'])) {
6643
7297
  this.schema['ui:order'] = this.schema.properties['ui:order'];
6644
7298
  delete this.schema.properties['ui:order'];
6645
7299
  }
6646
- this.ajv.removeSchema(this.schema);
6647
- this.validateFormData = this.ajv.compile(this.schema);
7300
+ this.getAjvInstance(ajvInstanceName).removeSchema(this.schema);
7301
+ ajvValidator = this.getAjvInstance(ajvInstanceName).compile(this.schema);
7302
+ this.ajvRegistry[ajvInstanceName].ajvValidator = ajvValidator;
6648
7303
  }
6649
7304
  }
6650
7305
  buildSchemaFromData(data, requireAllFields = false) {
@@ -6806,12 +7461,24 @@ class JsonSchemaFormService {
6806
7461
  if (!isObject(ctx)) {
6807
7462
  return false;
6808
7463
  }
7464
+ const layoutNode = ctx.layoutNode();
6809
7465
  if (isEmpty(ctx.options)) {
6810
- ctx.options = !isEmpty((ctx.layoutNode() || {}).options)
6811
- ? ctx.layoutNode().options
7466
+ ctx.options = !isEmpty((layoutNode || {}).options)
7467
+ ? layoutNode.options
6812
7468
  : cloneDeep(this.formOptions);
6813
7469
  }
6814
7470
  ctx.formControl = this.getFormControl(ctx);
7471
+ //introduced to check if the node is part of ITE conditional
7472
+ //then change or add the control
7473
+ if (layoutNode?.schemaPointer && layoutNode.isITEItem ||
7474
+ (layoutNode?.schemaPointer && layoutNode?.oneOfPointer)) {
7475
+ //before changing control, need to set the new data type for data formatting
7476
+ const schemaType = this.dataMap.get(layoutNode?.dataPointer).get("schemaType");
7477
+ if (schemaType != layoutNode.dataType) {
7478
+ this.dataMap.get(layoutNode?.dataPointer).set("schemaType", layoutNode.dataType);
7479
+ }
7480
+ this.setFormControl(ctx, ctx.formControl);
7481
+ }
6815
7482
  ctx.boundControl = bind && !!ctx.formControl;
6816
7483
  if (ctx.formControl) {
6817
7484
  ctx.controlName = this.getFormControlName(ctx);
@@ -6830,27 +7497,40 @@ class JsonSchemaFormService {
6830
7497
  ? null
6831
7498
  : this.formatErrors(ctx.formControl.errors, ctx.options.validationMessages)));
6832
7499
  this.fcValueChangesSubs = ctx.formControl.valueChanges.subscribe(value => {
6833
- //commented out to revert back to previous commits
6834
- //as seems to be causing some issues
6835
- /*
6836
- if (!!value) {
6837
- ctx.controlValue = value;
6838
- }
6839
- */
6840
- //TODO-test,this is the original code
6841
7500
  if (!isEqual$1(ctx.controlValue, value)) {
6842
7501
  ctx.controlValue = value;
6843
7502
  }
6844
7503
  });
6845
7504
  }
6846
7505
  else {
6847
- ctx.controlName = ctx.layoutNode().name;
6848
- ctx.controlValue = ctx.layoutNode().value || null;
7506
+ ctx.controlName = layoutNode.name;
7507
+ ctx.controlValue = layoutNode.value || null;
6849
7508
  const dataPointer = this.getDataPointer(ctx);
6850
7509
  if (bind && dataPointer) {
6851
7510
  console.error(`warning: control "${dataPointer}" is not bound to the Angular FormGroup.`);
6852
7511
  }
6853
7512
  }
7513
+ //if this is a ITE conditional field, the value would not have been
7514
+ //set, as the control would only be initialized when the condition is true
7515
+ //TODO-review need to decide which of the data sets between data,formValues and default
7516
+ //to use for the value
7517
+ if (ctx.options?.condition || layoutNode?.oneOfPointer) {
7518
+ const dataPointer = this.getDataPointer(ctx);
7519
+ const controlValue = ctx.formControl.value;
7520
+ const dataValue = JsonPointer.has(this.data, dataPointer) ?
7521
+ JsonPointer.get(this.data, dataPointer) : undefined;
7522
+ const formValue = JsonPointer.has(this.formValues, dataPointer) ?
7523
+ JsonPointer.get(this.formValues, dataPointer) : undefined;
7524
+ const schemaDefault = ctx.options?.default;
7525
+ //if initial formValues was supplied and controlValue matches formValue then likely
7526
+ //control was initially created with the formValue then set value to data value
7527
+ //if no formValues was supplied and controlValue matches schemaDefault then likely
7528
+ //control was initially created with the default then set value to data value
7529
+ const value = this.formValues && isEqual$1(formValue, controlValue) ? dataValue
7530
+ : !this.formValues && isEqual$1(schemaDefault, controlValue) ? dataValue
7531
+ : schemaDefault;
7532
+ ctx.formControl?.patchValue(value);
7533
+ }
6854
7534
  return ctx.boundControl;
6855
7535
  }
6856
7536
  formatErrors(errors, validationMessages = {}) {
@@ -6932,13 +7612,50 @@ class JsonSchemaFormService {
6932
7612
  }
6933
7613
  formArray.markAsDirty();
6934
7614
  }
7615
+ updateArrayMultiSelectList(ctx, selectList) {
7616
+ this.updateArrayCheckboxList(ctx, selectList);
7617
+ /* const formArray = <UntypedFormArray>this.getFormControl(ctx);
7618
+
7619
+ // Remove all existing items
7620
+ while (formArray.value.length) {
7621
+ formArray.removeAt(0);
7622
+ }
7623
+
7624
+ // Re-add an item for each checked box
7625
+ const refPointer = removeRecursiveReferences(
7626
+ ctx.layoutNode().dataPointer + '/-',
7627
+ this.dataRecursiveRefMap,
7628
+ this.arrayMap
7629
+ );
7630
+ for (const selectItem of selectList) {
7631
+ if (selectItem.value) {
7632
+ const newFormControl = buildFormGroup(
7633
+ this.templateRefLibrary[refPointer]
7634
+ );
7635
+ newFormControl.setValue(selectItem.value);
7636
+ formArray.push(newFormControl);
7637
+ }
7638
+ }
7639
+ formArray.markAsDirty();
7640
+ */
7641
+ }
6935
7642
  getFormControl(ctx) {
6936
7643
  if (!ctx || !ctx.layoutNode ||
6937
7644
  !isDefined(ctx.layoutNode().dataPointer) ||
6938
7645
  ctx.layoutNode().type === '$ref') {
6939
7646
  return null;
6940
7647
  }
6941
- return getControl(this.formGroup, this.getDataPointer(ctx));
7648
+ const schemaPointer = ctx.layoutNode()?.isITEItem ? ctx.layoutNode()?.schemaPointer : null;
7649
+ const oneOfPointer = ctx.layoutNode()?.oneOfPointer;
7650
+ return getControl(this.formGroup, this.getDataPointer(ctx), false, schemaPointer || oneOfPointer);
7651
+ }
7652
+ setFormControl(ctx, control) {
7653
+ if (!ctx || !ctx.layoutNode ||
7654
+ !isDefined(ctx.layoutNode().dataPointer) ||
7655
+ ctx.layoutNode().type === '$ref') {
7656
+ return null;
7657
+ }
7658
+ return setControl(this.formGroup, this.getDataPointer(ctx), control);
6942
7659
  }
6943
7660
  getFormControlValue(ctx) {
6944
7661
  if (!ctx || !ctx.layoutNode ||
@@ -6946,14 +7663,18 @@ class JsonSchemaFormService {
6946
7663
  ctx.layoutNode().type === '$ref') {
6947
7664
  return null;
6948
7665
  }
6949
- const control = getControl(this.formGroup, this.getDataPointer(ctx));
7666
+ const schemaPointer = ctx.layoutNode()?.isITEItem ? ctx.layoutNode()?.schemaPointer : null;
7667
+ const oneOfPointer = ctx.layoutNode()?.oneOfPointer;
7668
+ const control = getControl(this.formGroup, this.getDataPointer(ctx), false, schemaPointer || oneOfPointer);
6950
7669
  return control ? control.value : null;
6951
7670
  }
6952
7671
  getFormControlGroup(ctx) {
6953
7672
  if (!ctx || !ctx.layoutNode || !isDefined(ctx.layoutNode().dataPointer)) {
6954
7673
  return null;
6955
7674
  }
6956
- return getControl(this.formGroup, this.getDataPointer(ctx), true);
7675
+ const schemaPointer = ctx.layoutNode()?.isITEItem ? ctx.layoutNode()?.schemaPointer : null;
7676
+ const oneOfPointer = ctx.layoutNode()?.oneOfPointer;
7677
+ return getControl(this.formGroup, this.getDataPointer(ctx), true, schemaPointer || oneOfPointer);
6957
7678
  }
6958
7679
  getFormControlName(ctx) {
6959
7680
  if (!ctx || !ctx.layoutNode ||
@@ -7206,13 +7927,15 @@ class AddReferenceComponent {
7206
7927
  }
7207
7928
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddReferenceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7208
7929
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: AddReferenceComponent, selector: "add-reference-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7209
- <button *ngIf="showAddButton"
7210
- [class]="options?.fieldHtmlClass || ''"
7211
- [disabled]="options?.readonly"
7212
- (click)="addItem($event)">
7213
- <span *ngIf="options?.icon" [class]="options?.icon"></span>
7214
- <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7215
- </button>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
7930
+ <section [class]="options?.htmlClass || ''" align="end">
7931
+ <button *ngIf="showAddButton"
7932
+ [class]="options?.fieldHtmlClass || ''" class="sortable-filter sortable-fixed"
7933
+ [disabled]="options?.readonly"
7934
+ (click)="addItem($event)">
7935
+ <span *ngIf="options?.icon" [class]="options?.icon"></span>
7936
+ <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7937
+ </button>
7938
+ </section>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
7216
7939
  }
7217
7940
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddReferenceComponent, decorators: [{
7218
7941
  type: Component,
@@ -7220,13 +7943,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7220
7943
  // tslint:disable-next-line:component-selector
7221
7944
  selector: 'add-reference-widget',
7222
7945
  template: `
7223
- <button *ngIf="showAddButton"
7224
- [class]="options?.fieldHtmlClass || ''"
7225
- [disabled]="options?.readonly"
7226
- (click)="addItem($event)">
7227
- <span *ngIf="options?.icon" [class]="options?.icon"></span>
7228
- <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7229
- </button>`,
7946
+ <section [class]="options?.htmlClass || ''" align="end">
7947
+ <button *ngIf="showAddButton"
7948
+ [class]="options?.fieldHtmlClass || ''" class="sortable-filter sortable-fixed"
7949
+ [disabled]="options?.readonly"
7950
+ (click)="addItem($event)">
7951
+ <span *ngIf="options?.icon" [class]="options?.icon"></span>
7952
+ <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7953
+ </button>
7954
+ </section>`,
7230
7955
  changeDetection: ChangeDetectionStrategy.Default,
7231
7956
  }]
7232
7957
  }] });
@@ -7252,6 +7977,9 @@ class ButtonComponent {
7252
7977
  this.jsf.updateValue(this, event.target.value);
7253
7978
  }
7254
7979
  }
7980
+ ngOnDestroy() {
7981
+ this.jsf.updateValue(this, null);
7982
+ }
7255
7983
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7256
7984
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: ButtonComponent, selector: "button-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7257
7985
  <div
@@ -7296,6 +8024,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7296
8024
  }]
7297
8025
  }] });
7298
8026
 
8027
+ ///NB issue caused by sortablejs when it its destroyed
8028
+ //this mainly affects checkboxes coupled with conditions
8029
+ //-the value is rechecked
8030
+ //-see https://github.com/SortableJS/Sortable/issues/1052#issuecomment-369613072
7299
8031
  class CheckboxComponent {
7300
8032
  constructor() {
7301
8033
  this.jsf = inject(JsonSchemaFormService);
@@ -7311,7 +8043,8 @@ class CheckboxComponent {
7311
8043
  this.options = this.layoutNode().options || {};
7312
8044
  this.jsf.initializeControl(this);
7313
8045
  if (this.controlValue === null || this.controlValue === undefined) {
7314
- this.controlValue = this.options.title;
8046
+ this.controlValue = false;
8047
+ this.jsf.updateValue(this, this.falseValue);
7315
8048
  }
7316
8049
  }
7317
8050
  updateValue(event) {
@@ -7321,6 +8054,9 @@ class CheckboxComponent {
7321
8054
  get isChecked() {
7322
8055
  return this.jsf.getFormControlValue(this) === this.trueValue;
7323
8056
  }
8057
+ ngOnDestroy() {
8058
+ this.jsf.updateValue(this, null);
8059
+ }
7324
8060
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7325
8061
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: CheckboxComponent, selector: "checkbox-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7326
8062
  <label
@@ -7338,7 +8074,7 @@ class CheckboxComponent {
7338
8074
  type="checkbox">
7339
8075
  <input *ngIf="!boundControl"
7340
8076
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7341
- [checked]="isChecked ? 'checked' : null"
8077
+ [checked]="isChecked"
7342
8078
  [class]="(options?.fieldHtmlClass || '') + (isChecked ?
7343
8079
  (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
7344
8080
  (' ' + (options?.style?.unselected || '')))"
@@ -7375,7 +8111,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7375
8111
  type="checkbox">
7376
8112
  <input *ngIf="!boundControl"
7377
8113
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
7378
- [checked]="isChecked ? 'checked' : null"
8114
+ [checked]="isChecked"
7379
8115
  [class]="(options?.fieldHtmlClass || '') + (isChecked ?
7380
8116
  (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
7381
8117
  (' ' + (options?.style?.unselected || '')))"
@@ -7425,6 +8161,13 @@ class CheckboxesComponent {
7425
8161
  this.jsf.updateArrayCheckboxList(this, this.checkboxList);
7426
8162
  }
7427
8163
  }
8164
+ //TODO review this
8165
+ ngOnDestroy() {
8166
+ //this.jsf.updateValue(this, null);
8167
+ let nullVal = [];
8168
+ this.formControl.reset(nullVal);
8169
+ this.controlValue = null;
8170
+ }
7428
8171
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CheckboxesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7429
8172
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: CheckboxesComponent, selector: "checkboxes-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7430
8173
  <label *ngIf="options?.title"
@@ -7550,6 +8293,9 @@ class FileComponent {
7550
8293
  updateValue(event) {
7551
8294
  this.jsf.updateValue(this, event.target.value);
7552
8295
  }
8296
+ ngOnDestroy() {
8297
+ this.jsf.updateValue(this, null);
8298
+ }
7553
8299
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7554
8300
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: FileComponent, selector: "file-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ``, isInline: true }); }
7555
8301
  }
@@ -7574,6 +8320,9 @@ class HiddenComponent {
7574
8320
  ngOnInit() {
7575
8321
  this.jsf.initializeControl(this);
7576
8322
  }
8323
+ ngOnDestroy() {
8324
+ this.jsf.updateValue(this, null);
8325
+ }
7577
8326
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HiddenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7578
8327
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: HiddenComponent, selector: "hidden-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7579
8328
  <input *ngIf="boundControl"
@@ -7629,6 +8378,9 @@ class InputComponent {
7629
8378
  updateValue(event) {
7630
8379
  this.jsf.updateValue(this, event.target.value);
7631
8380
  }
8381
+ ngOnDestroy() {
8382
+ this.jsf.updateValue(this, null);
8383
+ }
7632
8384
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7633
8385
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: InputComponent, selector: "input-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7634
8386
  <div [class]="options?.htmlClass || ''" class="sortable-filter" >
@@ -7808,6 +8560,9 @@ class NumberComponent {
7808
8560
  updateValue(event) {
7809
8561
  this.jsf.updateValue(this, event.target.value);
7810
8562
  }
8563
+ ngOnDestroy() {
8564
+ this.jsf.updateValue(this, null);
8565
+ }
7811
8566
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NumberComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7812
8567
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: NumberComponent, selector: "number-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "inputControl", first: true, predicate: ["inputControl"], descendants: true }, { propertyName: "div", first: true, predicate: ["divElt"], descendants: true }], ngImport: i0, template: `
7813
8568
  <div #divElt [class]="options?.htmlClass || ''" class="sortable-filter" >
@@ -7916,6 +8671,194 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7916
8671
  args: ['divElt', {}]
7917
8672
  }] } });
7918
8673
 
8674
+ class SelectFrameworkComponent {
8675
+ constructor() {
8676
+ this.jsf = inject(JsonSchemaFormService);
8677
+ this.newComponent = null;
8678
+ this.layoutNode = input(undefined);
8679
+ this.layoutIndex = input(undefined);
8680
+ this.dataIndex = input(undefined);
8681
+ this.widgetContainer = viewChild('widgetContainer', { read: ViewContainerRef });
8682
+ }
8683
+ ngOnInit() {
8684
+ this.updateComponent();
8685
+ }
8686
+ ngOnChanges() {
8687
+ this.updateComponent();
8688
+ }
8689
+ updateComponent() {
8690
+ const widgetContainer = this.widgetContainer();
8691
+ if (widgetContainer && !this.newComponent && this.jsf.framework) {
8692
+ this.newComponent = widgetContainer.createComponent((this.jsf.framework));
8693
+ //TODO fix all deprecated calls and test
8694
+ //this.widgetContainer.createComponent<any>(this.jsf.framework)
8695
+ }
8696
+ if (this.newComponent) {
8697
+ for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
8698
+ this.newComponent.instance[input] = this[input];
8699
+ }
8700
+ }
8701
+ }
8702
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectFrameworkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8703
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "18.2.13", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "widgetContainer", first: true, predicate: ["widgetContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `<div #widgetContainer></div>`, isInline: true }); }
8704
+ }
8705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectFrameworkComponent, decorators: [{
8706
+ type: Component,
8707
+ args: [{
8708
+ // tslint:disable-next-line:component-selector
8709
+ selector: 'select-framework-widget',
8710
+ template: `<div #widgetContainer></div>`,
8711
+ }]
8712
+ }] });
8713
+
8714
+ class TabsComponent {
8715
+ constructor() {
8716
+ this.jsf = inject(JsonSchemaFormService);
8717
+ this.selectedItem = 0;
8718
+ this.showAddTab = true;
8719
+ this.layoutNode = input(undefined);
8720
+ this.layoutIndex = input(undefined);
8721
+ this.dataIndex = input(undefined);
8722
+ }
8723
+ ngOnInit() {
8724
+ this.options = this.layoutNode().options || {};
8725
+ if (this.options.selectedTab) {
8726
+ this.selectedItem = this.options.selectedTab;
8727
+ }
8728
+ this.itemCount = this.layoutNode().items.length - 1;
8729
+ this.updateControl();
8730
+ }
8731
+ select(index) {
8732
+ const layoutNode = this.layoutNode();
8733
+ if (layoutNode.items[index].type === '$ref') {
8734
+ this.itemCount = layoutNode.items.length;
8735
+ this.jsf.addItem({
8736
+ layoutNode: signal(layoutNode.items[index]),
8737
+ layoutIndex: signal(this.layoutIndex().concat(index)),
8738
+ dataIndex: signal(this.dataIndex().concat(index))
8739
+ });
8740
+ this.updateControl();
8741
+ }
8742
+ this.selectedItem = index;
8743
+ }
8744
+ updateControl() {
8745
+ const lastItem = this.layoutNode().items[this.layoutNode().items.length - 1];
8746
+ if (lastItem.type === '$ref' &&
8747
+ this.itemCount >= (lastItem.options.maxItems || 1000)) {
8748
+ this.showAddTab = false;
8749
+ }
8750
+ }
8751
+ setTabTitle(item, index) {
8752
+ return this.jsf.setArrayItemTitle(this, item, index);
8753
+ }
8754
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8755
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: TabsComponent, selector: "tabs-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
8756
+ <ul
8757
+ [class]="options?.labelHtmlClass || ''">
8758
+ <li *ngFor="let item of layoutNode()?.items; let i = index"
8759
+ [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8760
+ (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8761
+ (' ' + options?.style?.unselected))"
8762
+ role="presentation"
8763
+ data-tabs>
8764
+ <a *ngIf="showAddTab || item.type !== '$ref'"
8765
+ [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8766
+ (' ' + options?.style?.unselected))"
8767
+ (click)="select(i)">
8768
+ <input type="radio" [value]="i" *ngIf="options?.tabMode=='oneOfMode'"
8769
+ name="tabSelection"
8770
+ [(ngModel)]="selectedItem"
8771
+ [class]="(options?.widget_radioClass || '')"
8772
+ [value]="i"
8773
+ (change)="select(i)"
8774
+ />
8775
+ {{setTabTitle(item, i)}}
8776
+ </a>
8777
+ </li>
8778
+ </ul>
8779
+
8780
+ <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8781
+ [class]="(options?.htmlClass || '') + (selectedItem != i?' ngf-hidden':'') ">
8782
+ <!--for now the only difference between oneOfMode and the default
8783
+ is that oneOfMode uses the *ngIf="selectedItem === i" clause, which automatically
8784
+ destroys the tabs that are not rendered while default mode only hide them
8785
+ the upshot is that only the active tabs value will be used
8786
+ -->
8787
+ <ng-container *ngIf="options?.tabMode=='oneOfMode'">
8788
+ <select-framework-widget *ngIf="selectedItem === i"
8789
+ [class]="(options?.fieldHtmlClass || '') +
8790
+ ' ' + (options?.activeClass || '') +
8791
+ ' ' + (options?.style?.selected || '')"
8792
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8793
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8794
+ [layoutNode]="layoutItem"></select-framework-widget>
8795
+ </ng-container>
8796
+ <ng-container *ngIf="options?.tabMode !='oneOfMode'">
8797
+ <select-framework-widget
8798
+ [class]="(options?.fieldHtmlClass || '') +
8799
+ ' ' + (options?.activeClass || '') +
8800
+ ' ' + (options?.style?.selected || '')"
8801
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8802
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8803
+ [layoutNode]="layoutItem"></select-framework-widget>
8804
+ </ng-container>
8805
+ </div>`, isInline: true, styles: ["a{cursor:pointer}.ngf-hidden{display:none}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
8806
+ }
8807
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabsComponent, decorators: [{
8808
+ type: Component,
8809
+ args: [{ selector: 'tabs-widget', template: `
8810
+ <ul
8811
+ [class]="options?.labelHtmlClass || ''">
8812
+ <li *ngFor="let item of layoutNode()?.items; let i = index"
8813
+ [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8814
+ (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8815
+ (' ' + options?.style?.unselected))"
8816
+ role="presentation"
8817
+ data-tabs>
8818
+ <a *ngIf="showAddTab || item.type !== '$ref'"
8819
+ [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8820
+ (' ' + options?.style?.unselected))"
8821
+ (click)="select(i)">
8822
+ <input type="radio" [value]="i" *ngIf="options?.tabMode=='oneOfMode'"
8823
+ name="tabSelection"
8824
+ [(ngModel)]="selectedItem"
8825
+ [class]="(options?.widget_radioClass || '')"
8826
+ [value]="i"
8827
+ (change)="select(i)"
8828
+ />
8829
+ {{setTabTitle(item, i)}}
8830
+ </a>
8831
+ </li>
8832
+ </ul>
8833
+
8834
+ <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8835
+ [class]="(options?.htmlClass || '') + (selectedItem != i?' ngf-hidden':'') ">
8836
+ <!--for now the only difference between oneOfMode and the default
8837
+ is that oneOfMode uses the *ngIf="selectedItem === i" clause, which automatically
8838
+ destroys the tabs that are not rendered while default mode only hide them
8839
+ the upshot is that only the active tabs value will be used
8840
+ -->
8841
+ <ng-container *ngIf="options?.tabMode=='oneOfMode'">
8842
+ <select-framework-widget *ngIf="selectedItem === i"
8843
+ [class]="(options?.fieldHtmlClass || '') +
8844
+ ' ' + (options?.activeClass || '') +
8845
+ ' ' + (options?.style?.selected || '')"
8846
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8847
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8848
+ [layoutNode]="layoutItem"></select-framework-widget>
8849
+ </ng-container>
8850
+ <ng-container *ngIf="options?.tabMode !='oneOfMode'">
8851
+ <select-framework-widget
8852
+ [class]="(options?.fieldHtmlClass || '') +
8853
+ ' ' + (options?.activeClass || '') +
8854
+ ' ' + (options?.style?.selected || '')"
8855
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8856
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8857
+ [layoutNode]="layoutItem"></select-framework-widget>
8858
+ </ng-container>
8859
+ </div>`, standalone: false, styles: ["a{cursor:pointer}.ngf-hidden{display:none}\n"] }]
8860
+ }] });
8861
+
7919
8862
  // TODO: Add this control
7920
8863
  class OneOfComponent {
7921
8864
  constructor() {
@@ -7928,20 +8871,101 @@ class OneOfComponent {
7928
8871
  }
7929
8872
  ngOnInit() {
7930
8873
  this.options = this.layoutNode().options || {};
8874
+ this.options.tabMode = "oneOfMode";
8875
+ this.options.selectedTab = this.findSelectedTab();
8876
+ //this.options.description=this.options.description||"choose one of";
7931
8877
  this.jsf.initializeControl(this);
7932
8878
  }
8879
+ findSelectedTab() {
8880
+ //TODO test- this.jsf.formValues seems to be the initial data supplied to the form
8881
+ //while the jsf.formGroup value is derived from the actual controls
8882
+ //let formValue=this.jsf.getFormControlValue(this);
8883
+ let foundInd = -1;
8884
+ //seach for non null value
8885
+ if (this.layoutNode().items) {
8886
+ this.layoutNode().items.forEach((layoutItem, ind) => {
8887
+ let formValue = JsonPointer.get(this.jsf.formValues, layoutItem.dataPointer);
8888
+ if (layoutItem.oneOfPointer) {
8889
+ let controlKey = path2ControlKey(layoutItem.oneOfPointer);
8890
+ let fname = layoutItem.name;
8891
+ if (hasOwn(this.jsf.formGroup.controls, controlKey) &&
8892
+ (formValue || hasNonNullValue(this.jsf.formGroup.controls[controlKey].value))
8893
+ //hasOwn(formValue,fname) && hasOwn(this.jsf.formGroup.controls,controlKey)
8894
+ // && (formValue[fname] || this.jsf.formGroup.controls[controlKey].value)
8895
+ //&&isEqual(formValue[fname],this.jsf.formGroup.controls[controlKey].value)
8896
+ ) {
8897
+ foundInd = ind;
8898
+ }
8899
+ //foundInd=formValue[controlKey]!=null?ind:foundInd;
8900
+ //if no exact match found, then search in descendant values
8901
+ //to see which one of item matches
8902
+ if (foundInd == -1) {
8903
+ //find all descendant oneof paths
8904
+ let descendantOneOfControlNames = Object.keys(this.jsf.formGroup.controls).filter(controlName => {
8905
+ return controlName.startsWith(controlKey);
8906
+ });
8907
+ descendantOneOfControlNames.forEach(controlName => {
8908
+ let parts = controlName.split('$');
8909
+ let fieldName = parts[parts.length - 1];
8910
+ let controlValue = this.jsf.formGroup.controls[controlName].value;
8911
+ let controlSchema = JsonPointer.get(this.jsf.schema, parts.join("/"));
8912
+ let schemaPointer = parts.join("/");
8913
+ let dPointer = schemaPointer.replace(/(anyOf|allOf|oneOf|none)\/[\d]+\//g, '')
8914
+ .replace(/(if|then|else|properties)\//g, '');
8915
+ //JsonPointer.toDataPointer(parts.join("/"),this.jsf.schema);
8916
+ let dVal = JsonPointer.get(this.jsf.formValues, dPointer);
8917
+ let compareVal = dVal; //formValue;
8918
+ //compare only values that are in the subschema properties
8919
+ if (controlSchema && controlSchema.properties) {
8920
+ compareVal = isObject$1(dVal) && hasOwn(dVal, fieldName) ?
8921
+ pick(dVal[fieldName], Object.keys(controlSchema.properties))
8922
+ : pick(dVal, Object.keys(controlSchema.properties));
8923
+ }
8924
+ /*
8925
+ if(isObject(compareVal) && hasOwn(compareVal,fieldName) &&
8926
+ isEqual(compareVal[fieldName],controlValue)
8927
+ ){
8928
+ foundInd=ind;
8929
+ }else //if(formValue || controlValue){
8930
+ if(isEqual(compareVal,controlValue)){
8931
+ foundInd=ind;
8932
+ }
8933
+ */
8934
+ if (isEqual$2(compareVal, controlValue)) {
8935
+ foundInd = ind;
8936
+ }
8937
+ });
8938
+ //now need to compare values
8939
+ }
8940
+ }
8941
+ });
8942
+ }
8943
+ return Math.max(foundInd, 0);
8944
+ }
7933
8945
  updateValue(event) {
7934
8946
  this.jsf.updateValue(this, event.target.value);
7935
8947
  }
8948
+ ngOnDestroy() {
8949
+ //this.jsf.updateValue(this, null);
8950
+ }
7936
8951
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OneOfComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7937
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: OneOfComponent, selector: "one-of-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ``, isInline: true }); }
8952
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: OneOfComponent, selector: "one-of-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `<h4>{{this.options?.description}}</h4>
8953
+ <tabs-widget #tabs [layoutNode]="layoutNode()"
8954
+ [layoutIndex]="layoutIndex()"
8955
+ [dataIndex]="dataIndex()" >
8956
+ </tabs-widget>`, isInline: true, dependencies: [{ kind: "component", type: TabsComponent, selector: "tabs-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
7938
8957
  }
7939
8958
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OneOfComponent, decorators: [{
7940
8959
  type: Component,
7941
8960
  args: [{
7942
8961
  // tslint:disable-next-line:component-selector
7943
8962
  selector: 'one-of-widget',
7944
- template: ``,
8963
+ template: `<h4>{{this.options?.description}}</h4>
8964
+ <tabs-widget #tabs [layoutNode]="layoutNode()"
8965
+ [layoutIndex]="layoutIndex()"
8966
+ [dataIndex]="dataIndex()" >
8967
+ </tabs-widget>`,
8968
+ standalone: false
7945
8969
  }]
7946
8970
  }] });
7947
8971
 
@@ -7969,6 +8993,9 @@ class RadiosComponent {
7969
8993
  updateValue(event) {
7970
8994
  this.jsf.updateValue(this, event.target.value);
7971
8995
  }
8996
+ ngOnDestroy() {
8997
+ this.jsf.updateValue(this, null);
8998
+ }
7972
8999
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RadiosComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7973
9000
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: RadiosComponent, selector: "radios-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
7974
9001
  <label *ngIf="options?.title"
@@ -8091,46 +9118,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8091
9118
  }]
8092
9119
  }] });
8093
9120
 
8094
- class SelectFrameworkComponent {
8095
- constructor() {
8096
- this.jsf = inject(JsonSchemaFormService);
8097
- this.newComponent = null;
8098
- this.layoutNode = input(undefined);
8099
- this.layoutIndex = input(undefined);
8100
- this.dataIndex = input(undefined);
8101
- this.widgetContainer = viewChild('widgetContainer', { read: ViewContainerRef });
8102
- }
8103
- ngOnInit() {
8104
- this.updateComponent();
8105
- }
8106
- ngOnChanges() {
8107
- this.updateComponent();
8108
- }
8109
- updateComponent() {
8110
- const widgetContainer = this.widgetContainer();
8111
- if (widgetContainer && !this.newComponent && this.jsf.framework) {
8112
- this.newComponent = widgetContainer.createComponent((this.jsf.framework));
8113
- //TODO fix all deprecated calls and test
8114
- //this.widgetContainer.createComponent<any>(this.jsf.framework)
8115
- }
8116
- if (this.newComponent) {
8117
- for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
8118
- this.newComponent.instance[input] = this[input];
8119
- }
8120
- }
8121
- }
8122
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectFrameworkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8123
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "18.2.13", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "widgetContainer", first: true, predicate: ["widgetContainer"], descendants: true, read: ViewContainerRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `<div #widgetContainer></div>`, isInline: true }); }
8124
- }
8125
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectFrameworkComponent, decorators: [{
8126
- type: Component,
8127
- args: [{
8128
- // tslint:disable-next-line:component-selector
8129
- selector: 'select-framework-widget',
8130
- template: `<div #widgetContainer></div>`,
8131
- }]
8132
- }] });
8133
-
8134
9121
  /**
8135
9122
  * OrderableDirective
8136
9123
  *
@@ -8286,11 +9273,74 @@ class RootComponent {
8286
9273
  };
8287
9274
  //must set moveLayout to false as nxtSortable already moves it
8288
9275
  this.jsf.moveArrayItem(itemCtx, evt.oldIndex, evt.newIndex, false);
9276
+ },
9277
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
9278
+ if (evt.related.classList.contains("sortable-fixed")) {
9279
+ //console.log(evt.related);
9280
+ return false;
9281
+ }
8289
9282
  }
8290
9283
  };
8291
9284
  }
8292
9285
  sortableInit(sortable) {
8293
9286
  this.sortableObj = sortable;
9287
+ ///NB issue caused by sortablejs when it its destroyed
9288
+ //this mainly affects checkboxes coupled with conditions
9289
+ //-the value is rechecked
9290
+ //-see https://github.com/SortableJS/Sortable/issues/1052#issuecomment-369613072
9291
+ /* attempt to monkey patch sortable js
9292
+ const originalMethod = sortable._nulling;
9293
+ let zone=this.zone;
9294
+ sortable._nulling=function() {
9295
+ console.log(`pluginEvent 2 ${pluginEvent}`)
9296
+ zone.runOutsideAngular(() => {
9297
+ console.log(`pluginEvent3 ${pluginEvent}`)
9298
+ pluginEvent('nulling', this);
9299
+
9300
+ rootEl =
9301
+ dragEl =
9302
+ parentEl =
9303
+ ghostEl =
9304
+ nextEl =
9305
+ cloneEl =
9306
+ lastDownEl =
9307
+ cloneHidden =
9308
+
9309
+ tapEvt =
9310
+ touchEvt =
9311
+
9312
+ moved =
9313
+ newIndex =
9314
+ newDraggableIndex =
9315
+ oldIndex =
9316
+ oldDraggableIndex =
9317
+
9318
+ lastTarget =
9319
+ lastDirection =
9320
+
9321
+ putSortable =
9322
+ activeGroup =
9323
+ Sortable.dragged =
9324
+ Sortable.ghost =
9325
+ Sortable.clone =
9326
+ Sortable.active = null;
9327
+
9328
+
9329
+ let el = this.el;
9330
+ savedInputChecked.forEach(function (checkEl) {
9331
+ if (el.contains(checkEl)) {
9332
+ checkEl.checked = true;
9333
+ }
9334
+ });
9335
+
9336
+ savedInputChecked.length =
9337
+ lastDx =
9338
+ lastDy = 0;
9339
+
9340
+ })
9341
+
9342
+ }.bind(sortable)
9343
+ */
8294
9344
  }
8295
9345
  isDraggable(node) {
8296
9346
  let result = node.arrayItem && node.type !== '$ref' &&
@@ -8302,6 +9352,12 @@ class RootComponent {
8302
9352
  }
8303
9353
  return result;
8304
9354
  }
9355
+ //TODO also need to think of other types such as button which can be
9356
+ //created by an arbitrary layout
9357
+ isFixed(node) {
9358
+ let result = node.type == '$ref';
9359
+ return result;
9360
+ }
8305
9361
  // Set attributes for flexbox child
8306
9362
  // (container attributes are set in section.component)
8307
9363
  getFlexAttribute(node, attribute) {
@@ -8341,6 +9397,7 @@ class RootComponent {
8341
9397
  [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8342
9398
  [style.order]="(layoutItem.options || {}).order"
8343
9399
  [class.sortable-filter]="!isDraggable(layoutItem)"
9400
+ [class.sortable-fixed]="isFixed(layoutItem)"
8344
9401
  >
8345
9402
  <!--NB orderable directive is not used but has been left in for now and set to false
8346
9403
  otherwise the compiler won't recognize dataIndex and other dependent attributes
@@ -8351,6 +9408,7 @@ class RootComponent {
8351
9408
  [layoutNode]="layoutItem"
8352
9409
  [orderable]="false"
8353
9410
  [class.sortable-filter]="!isDraggable(layoutItem)"
9411
+ [class.sortable-fixed]="isFixed(layoutItem)"
8354
9412
  >
8355
9413
  <select-framework-widget *ngIf="showWidget(layoutItem)"
8356
9414
  [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
@@ -8373,6 +9431,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8373
9431
  [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8374
9432
  [style.order]="(layoutItem.options || {}).order"
8375
9433
  [class.sortable-filter]="!isDraggable(layoutItem)"
9434
+ [class.sortable-fixed]="isFixed(layoutItem)"
8376
9435
  >
8377
9436
  <!--NB orderable directive is not used but has been left in for now and set to false
8378
9437
  otherwise the compiler won't recognize dataIndex and other dependent attributes
@@ -8383,6 +9442,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8383
9442
  [layoutNode]="layoutItem"
8384
9443
  [orderable]="false"
8385
9444
  [class.sortable-filter]="!isDraggable(layoutItem)"
9445
+ [class.sortable-fixed]="isFixed(layoutItem)"
8386
9446
  >
8387
9447
  <select-framework-widget *ngIf="showWidget(layoutItem)"
8388
9448
  [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
@@ -8591,6 +9651,7 @@ class SelectComponent {
8591
9651
  this.controlDisabled = false;
8592
9652
  this.boundControl = false;
8593
9653
  this.selectList = [];
9654
+ this.selectListFlatGroup = [];
8594
9655
  this.isArray = isArray;
8595
9656
  this.layoutNode = input(undefined);
8596
9657
  this.layoutIndex = input(undefined);
@@ -8599,10 +9660,40 @@ class SelectComponent {
8599
9660
  ngOnInit() {
8600
9661
  this.options = this.layoutNode().options || {};
8601
9662
  this.selectList = buildTitleMap(this.options.titleMap || this.options.enumNames, this.options.enum, !!this.options.required, !!this.options.flatList);
9663
+ //the selectListFlatGroup array will be used to update the formArray values
9664
+ //while the selectList array will be bound to the form select
9665
+ //as either a grouped select or a flat select
9666
+ this.selectListFlatGroup = buildTitleMap(this.options.titleMap || this.options.enumNames, this.options.enum, !!this.options.required, true);
8602
9667
  this.jsf.initializeControl(this);
8603
9668
  }
9669
+ deselectAll() {
9670
+ this.selectListFlatGroup.forEach(selItem => {
9671
+ selItem.checked = false;
9672
+ });
9673
+ }
8604
9674
  updateValue(event) {
8605
- this.jsf.updateValue(this, event.target.value);
9675
+ this.options.showErrors = true;
9676
+ if (this.options.multiple) {
9677
+ if (this.controlValue?.includes(null)) {
9678
+ this.deselectAll();
9679
+ //this.control.setValue([]); // Reset the form control to an empty array
9680
+ //this.selectList=JSON.parse(JSON.stringify(this.selectList));
9681
+ this.jsf.updateArrayMultiSelectList(this, []);
9682
+ }
9683
+ else {
9684
+ this.selectListFlatGroup.forEach(selItem => {
9685
+ selItem.checked = this.controlValue?.indexOf(selItem.value) >= 0 ? true : false;
9686
+ });
9687
+ this.jsf.updateArrayMultiSelectList(this, this.selectListFlatGroup);
9688
+ }
9689
+ return;
9690
+ }
9691
+ this.jsf.updateValue(this, this.controlValue);
9692
+ }
9693
+ ngOnDestroy() {
9694
+ let nullVal = this.options.multiple ? [null] : null;
9695
+ this.formControl.reset(nullVal);
9696
+ this.controlValue = null;
8606
9697
  }
8607
9698
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8608
9699
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: SelectComponent, selector: "select-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
@@ -8613,7 +9704,7 @@ class SelectComponent {
8613
9704
  [class]="options?.labelHtmlClass || ''"
8614
9705
  [style.display]="options?.notitle ? 'none' : ''"
8615
9706
  [innerHTML]="options?.title"></label>
8616
- <select *ngIf="boundControl"
9707
+ <select *ngIf="boundControl && !options?.multiple"
8617
9708
  [formControl]="formControl"
8618
9709
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
8619
9710
  [attr.readonly]="options?.readonly ? 'readonly' : null"
@@ -8660,7 +9751,34 @@ class SelectComponent {
8660
9751
  </optgroup>
8661
9752
  </ng-template>
8662
9753
  </select>
8663
- </div>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
9754
+ <select *ngIf="boundControl && options?.multiple"
9755
+ [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
9756
+ [attr.readonly]="options?.readonly ? 'readonly' : null"
9757
+ [attr.required]="options?.required"
9758
+ [class]="options?.fieldHtmlClass || ''"
9759
+ [disabled]="controlDisabled"
9760
+ [id]="'control' + layoutNode()?._id"
9761
+ [multiple]="options?.multiple"
9762
+ [name]="controlName"
9763
+ [(ngModel)]="controlValue"
9764
+ (change)="updateValue($event)">
9765
+ <ng-template ngFor let-selectItem [ngForOf]="selectList">
9766
+ <option *ngIf="!isArray(selectItem?.items)"
9767
+ [selected]="selectItem?.value === controlValue"
9768
+ [value]="selectItem?.value">
9769
+ <span [innerHTML]="selectItem?.name"></span>
9770
+ </option>
9771
+ <optgroup *ngIf="isArray(selectItem?.items)"
9772
+ [label]="selectItem?.group">
9773
+ <option *ngFor="let subItem of selectItem.items"
9774
+ [attr.selected]="subItem?.value === controlValue"
9775
+ [value]="subItem?.value">
9776
+ <span [innerHTML]="subItem?.name"></span>
9777
+ </option>
9778
+ </optgroup>
9779
+ </ng-template>
9780
+ </select>
9781
+ </div>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.SelectMultipleControlValueAccessor, selector: "select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] }); }
8664
9782
  }
8665
9783
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, decorators: [{
8666
9784
  type: Component,
@@ -8675,7 +9793,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8675
9793
  [class]="options?.labelHtmlClass || ''"
8676
9794
  [style.display]="options?.notitle ? 'none' : ''"
8677
9795
  [innerHTML]="options?.title"></label>
8678
- <select *ngIf="boundControl"
9796
+ <select *ngIf="boundControl && !options?.multiple"
8679
9797
  [formControl]="formControl"
8680
9798
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
8681
9799
  [attr.readonly]="options?.readonly ? 'readonly' : null"
@@ -8722,6 +9840,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8722
9840
  </optgroup>
8723
9841
  </ng-template>
8724
9842
  </select>
9843
+ <select *ngIf="boundControl && options?.multiple"
9844
+ [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
9845
+ [attr.readonly]="options?.readonly ? 'readonly' : null"
9846
+ [attr.required]="options?.required"
9847
+ [class]="options?.fieldHtmlClass || ''"
9848
+ [disabled]="controlDisabled"
9849
+ [id]="'control' + layoutNode()?._id"
9850
+ [multiple]="options?.multiple"
9851
+ [name]="controlName"
9852
+ [(ngModel)]="controlValue"
9853
+ (change)="updateValue($event)">
9854
+ <ng-template ngFor let-selectItem [ngForOf]="selectList">
9855
+ <option *ngIf="!isArray(selectItem?.items)"
9856
+ [selected]="selectItem?.value === controlValue"
9857
+ [value]="selectItem?.value">
9858
+ <span [innerHTML]="selectItem?.name"></span>
9859
+ </option>
9860
+ <optgroup *ngIf="isArray(selectItem?.items)"
9861
+ [label]="selectItem?.group">
9862
+ <option *ngFor="let subItem of selectItem.items"
9863
+ [attr.selected]="subItem?.value === controlValue"
9864
+ [value]="subItem?.value">
9865
+ <span [innerHTML]="subItem?.name"></span>
9866
+ </option>
9867
+ </optgroup>
9868
+ </ng-template>
9869
+ </select>
8725
9870
  </div>`,
8726
9871
  }]
8727
9872
  }] });
@@ -8738,6 +9883,7 @@ class SubmitComponent {
8738
9883
  ngOnDestroy() {
8739
9884
  this.isValidChangesSubs?.unsubscribe();
8740
9885
  this.isValidChangesSubs = null;
9886
+ this.updateValue({ target: { value: null } });
8741
9887
  }
8742
9888
  ngOnInit() {
8743
9889
  this.options = this.layoutNode().options || {};
@@ -8835,107 +9981,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8835
9981
  }]
8836
9982
  }] });
8837
9983
 
8838
- class TabsComponent {
8839
- constructor() {
8840
- this.jsf = inject(JsonSchemaFormService);
8841
- this.selectedItem = 0;
8842
- this.showAddTab = true;
8843
- this.layoutNode = input(undefined);
8844
- this.layoutIndex = input(undefined);
8845
- this.dataIndex = input(undefined);
8846
- }
8847
- ngOnInit() {
8848
- this.options = this.layoutNode().options || {};
8849
- this.itemCount = this.layoutNode().items.length - 1;
8850
- this.updateControl();
8851
- }
8852
- select(index) {
8853
- const layoutNode = this.layoutNode();
8854
- if (layoutNode.items[index].type === '$ref') {
8855
- this.itemCount = layoutNode.items.length;
8856
- this.jsf.addItem({
8857
- layoutNode: signal(layoutNode.items[index]),
8858
- layoutIndex: signal(this.layoutIndex().concat(index)),
8859
- dataIndex: signal(this.dataIndex().concat(index))
8860
- });
8861
- this.updateControl();
8862
- }
8863
- this.selectedItem = index;
8864
- }
8865
- updateControl() {
8866
- const lastItem = this.layoutNode().items[this.layoutNode().items.length - 1];
8867
- if (lastItem.type === '$ref' &&
8868
- this.itemCount >= (lastItem.options.maxItems || 1000)) {
8869
- this.showAddTab = false;
8870
- }
8871
- }
8872
- setTabTitle(item, index) {
8873
- return this.jsf.setArrayItemTitle(this, item, index);
8874
- }
8875
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8876
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: TabsComponent, selector: "tabs-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
8877
- <ul
8878
- [class]="options?.labelHtmlClass || ''">
8879
- <li *ngFor="let item of layoutNode()?.items; let i = index"
8880
- [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8881
- (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8882
- (' ' + options?.style?.unselected))"
8883
- role="presentation"
8884
- data-tabs>
8885
- <a *ngIf="showAddTab || item.type !== '$ref'"
8886
- [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8887
- (' ' + options?.style?.unselected))"
8888
- [innerHTML]="setTabTitle(item, i)"
8889
- (click)="select(i)"></a>
8890
- </li>
8891
- </ul>
8892
-
8893
- <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8894
- [class]="options?.htmlClass || ''">
8895
-
8896
- <select-framework-widget *ngIf="selectedItem === i"
8897
- [class]="(options?.fieldHtmlClass || '') +
8898
- ' ' + (options?.activeClass || '') +
8899
- ' ' + (options?.style?.selected || '')"
8900
- [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8901
- [layoutIndex]="(layoutIndex() || []).concat(i)"
8902
- [layoutNode]="layoutItem"></select-framework-widget>
8903
-
8904
- </div>`, isInline: true, styles: ["a{cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SelectFrameworkComponent, selector: "select-framework-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
8905
- }
8906
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabsComponent, decorators: [{
8907
- type: Component,
8908
- args: [{ selector: 'tabs-widget', template: `
8909
- <ul
8910
- [class]="options?.labelHtmlClass || ''">
8911
- <li *ngFor="let item of layoutNode()?.items; let i = index"
8912
- [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8913
- (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8914
- (' ' + options?.style?.unselected))"
8915
- role="presentation"
8916
- data-tabs>
8917
- <a *ngIf="showAddTab || item.type !== '$ref'"
8918
- [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8919
- (' ' + options?.style?.unselected))"
8920
- [innerHTML]="setTabTitle(item, i)"
8921
- (click)="select(i)"></a>
8922
- </li>
8923
- </ul>
8924
-
8925
- <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8926
- [class]="options?.htmlClass || ''">
8927
-
8928
- <select-framework-widget *ngIf="selectedItem === i"
8929
- [class]="(options?.fieldHtmlClass || '') +
8930
- ' ' + (options?.activeClass || '') +
8931
- ' ' + (options?.style?.selected || '')"
8932
- [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8933
- [layoutIndex]="(layoutIndex() || []).concat(i)"
8934
- [layoutNode]="layoutItem"></select-framework-widget>
8935
-
8936
- </div>`, styles: ["a{cursor:pointer}\n"] }]
8937
- }] });
8938
-
8939
9984
  class TemplateComponent {
8940
9985
  constructor() {
8941
9986
  this.jsf = inject(JsonSchemaFormService);
@@ -8991,6 +10036,9 @@ class TextareaComponent {
8991
10036
  updateValue(event) {
8992
10037
  this.jsf.updateValue(this, event.target.value);
8993
10038
  }
10039
+ ngOnDestroy() {
10040
+ this.jsf.updateValue(this, null);
10041
+ }
8994
10042
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8995
10043
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: TextareaComponent, selector: "textarea-widget", inputs: { layoutNode: { classPropertyName: "layoutNode", publicName: "layoutNode", isSignal: true, isRequired: false, transformFunction: null }, layoutIndex: { classPropertyName: "layoutIndex", publicName: "layoutIndex", isSignal: true, isRequired: false, transformFunction: null }, dataIndex: { classPropertyName: "dataIndex", publicName: "dataIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
8996
10044
  <div
@@ -9291,7 +10339,13 @@ class WidgetLibraryModule {
9291
10339
  //disabled:false,
9292
10340
  //draggable:".draggableitem",//">:not(.nonsort)",//">.draggable-item",//":not(.nonsort)",//">*",//":not(.nonsort)",//":not(.non-draggable)",
9293
10341
  filter: ".sortable-filter", //needed to disable dragging on input range elements, class needs to be added to the element or its parent
9294
- preventOnFilter: false //needed for input range elements slider do still work
10342
+ preventOnFilter: false, //needed for input range elements slider do still work
10343
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
10344
+ if (evt.related.classList.contains("sortable-fixed")) {
10345
+ //console.log(evt.related);
10346
+ return false;
10347
+ }
10348
+ }
9295
10349
  })] }); }
9296
10350
  }
9297
10351
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WidgetLibraryModule, decorators: [{
@@ -9302,7 +10356,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
9302
10356
  //disabled:false,
9303
10357
  //draggable:".draggableitem",//">:not(.nonsort)",//">.draggable-item",//":not(.nonsort)",//">*",//":not(.nonsort)",//":not(.non-draggable)",
9304
10358
  filter: ".sortable-filter", //needed to disable dragging on input range elements, class needs to be added to the element or its parent
9305
- preventOnFilter: false //needed for input range elements slider do still work
10359
+ preventOnFilter: false, //needed for input range elements slider do still work
10360
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
10361
+ if (evt.related.classList.contains("sortable-fixed")) {
10362
+ //console.log(evt.related);
10363
+ return false;
10364
+ }
10365
+ }
9306
10366
  })],
9307
10367
  declarations: [...BASIC_WIDGETS, OrderableDirective, ElementAttributeDirective],
9308
10368
  exports: [...BASIC_WIDGETS, OrderableDirective, ElementAttributeDirective]
@@ -9547,7 +10607,7 @@ class JsonSchemaFormComponent {
9547
10607
  this.previousInputs = {
9548
10608
  schema: null, layout: null, data: null, options: null, framework: null,
9549
10609
  widgets: null, form: null, model: null, JSONSchema: null, UISchema: null,
9550
- formData: null, loadExternalAssets: null, debug: null,
10610
+ formData: null, loadExternalAssets: null, debug: null, ajvOptions: null
9551
10611
  };
9552
10612
  // Recommended inputs
9553
10613
  this.schema = input(undefined); // The JSON Schema
@@ -9570,6 +10630,7 @@ class JsonSchemaFormComponent {
9570
10630
  this.loadExternalAssets = input(undefined); // Load external framework assets?
9571
10631
  this.debug = input(undefined); // Show debug information?
9572
10632
  this.theme = input(undefined); // Theme
10633
+ this.ajvOptions = input(undefined); // ajvOptions
9573
10634
  // Outputs
9574
10635
  this.onChanges = output(); // Live unvalidated internal form data
9575
10636
  this.onSubmit = output(); // Complete validated form data
@@ -9791,6 +10852,7 @@ class JsonSchemaFormComponent {
9791
10852
  this.jsf.data) {
9792
10853
  // Reset all form values to defaults
9793
10854
  this.jsf.resetAllValues();
10855
+ this.initializeAjv();
9794
10856
  this.initializeOptions(); // Update options
9795
10857
  this.initializeSchema(); // Update schema, schemaRefLibrary,
9796
10858
  // schemaRecursiveRefMap, & dataRecursiveRefMap
@@ -9841,6 +10903,20 @@ class JsonSchemaFormComponent {
9841
10903
  this.formInitialized = true;
9842
10904
  }
9843
10905
  }
10906
+ /**
10907
+ * 'initializeAjv' function
10908
+ *
10909
+ * Initialize ajv from 'ajvOptions'
10910
+ */
10911
+ initializeAjv() {
10912
+ const form = this.form();
10913
+ const ajvOptions = cloneDeep(this.ajvOptions()) ||
10914
+ (form && hasOwn(form, 'ajvOptions') && isObject(form.ajvOptions)
10915
+ && cloneDeep(form.ajvOptions));
10916
+ if (ajvOptions) {
10917
+ this.ajvInstanceName = this.jsf.createAndRegisterAjvInstance(ajvOptions).name;
10918
+ }
10919
+ }
9844
10920
  /**
9845
10921
  * 'initializeOptions' function
9846
10922
  *
@@ -9964,7 +11040,8 @@ class JsonSchemaFormComponent {
9964
11040
  // draft 3 (JSON Form style) and draft 4 (Angular Schema Form style)
9965
11041
  this.jsf.schema = convertSchemaToDraft6(this.jsf.schema);
9966
11042
  // Initialize ajv and compile schema
9967
- this.jsf.compileAjvSchema();
11043
+ //this.jsf.compileAjvSchema();
11044
+ //moved to initializeAjv()
9968
11045
  // Create schemaRefLibrary, schemaRecursiveRefMap, dataRecursiveRefMap, & arrayMap
9969
11046
  this.jsf.schema = resolveSchemaReferences(this.jsf.schema, this.jsf.schemaRefLibrary, this.jsf.schemaRecursiveRefMap, this.jsf.dataRecursiveRefMap, this.jsf.arrayMap);
9970
11047
  if (hasOwn(this.jsf.schemaRefLibrary, '')) {
@@ -10168,7 +11245,8 @@ class JsonSchemaFormComponent {
10168
11245
  }
10169
11246
  if (!isEmpty(this.jsf.schema)) {
10170
11247
  // If not already initialized, initialize ajv and compile schema
10171
- this.jsf.compileAjvSchema();
11248
+ //this.jsf.compileAjvSchema();
11249
+ //moved to initializeAjv()
10172
11250
  // Update all layout elements, add values, widgets, and validators,
10173
11251
  // replace any '*' with a layout built from all schema elements,
10174
11252
  // and update the FormGroup template with any new validators
@@ -10176,7 +11254,7 @@ class JsonSchemaFormComponent {
10176
11254
  // Build the Angular FormGroup template from the schema
10177
11255
  this.jsf.buildFormGroupTemplate(this.jsf.formValues);
10178
11256
  // Build the real Angular FormGroup from the FormGroup template
10179
- this.jsf.buildFormGroup();
11257
+ this.jsf.buildFormGroup(this.ajvInstanceName);
10180
11258
  }
10181
11259
  if (this.jsf.formGroup) {
10182
11260
  // Reset initial form values
@@ -10227,7 +11305,7 @@ class JsonSchemaFormComponent {
10227
11305
  }
10228
11306
  }
10229
11307
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: JsonSchemaFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10230
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: JsonSchemaFormComponent, selector: "json-schema-form", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, framework: { classPropertyName: "framework", publicName: "framework", isSignal: true, isRequired: false, transformFunction: null }, widgets: { classPropertyName: "widgets", publicName: "widgets", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, JSONSchema: { classPropertyName: "JSONSchema", publicName: "JSONSchema", isSignal: true, isRequired: false, transformFunction: null }, UISchema: { classPropertyName: "UISchema", publicName: "UISchema", isSignal: true, isRequired: false, transformFunction: null }, formData: { classPropertyName: "formData", publicName: "formData", isSignal: true, isRequired: false, transformFunction: null }, ngModel: { classPropertyName: "ngModel", publicName: "ngModel", isSignal: true, isRequired: false, transformFunction: null }, language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null }, loadExternalAssets: { classPropertyName: "loadExternalAssets", publicName: "loadExternalAssets", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onChanges: "onChanges", onSubmit: "onSubmit", isValid: "isValid", validationErrors: "validationErrors", formSchema: "formSchema", formLayout: "formLayout", dataChange: "dataChange", modelChange: "modelChange", formDataChange: "formDataChange", ngModelChange: "ngModelChange" }, providers: [JsonSchemaFormService, JSON_SCHEMA_FORM_VALUE_ACCESSOR], usesOnChanges: true, ngImport: i0, template: "<form [autocomplete]=\"jsf?.formOptions?.autocomplete ? 'on' : 'off'\" class=\"json-schema-form\" (ngSubmit)=\"submitForm()\">\r\n <root-widget [layout]=\"jsf?.layout\"></root-widget>\r\n</form>\r\n<div *ngIf=\"debug() || jsf?.formOptions?.debug\">\r\n Debug output:\r\n <pre>{{debugOutput}}</pre>\r\n</div>", dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: RootComponent, selector: "root-widget", inputs: ["dataIndex", "layoutIndex", "layout", "isOrderable", "isFlexItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11308
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: JsonSchemaFormComponent, selector: "json-schema-form", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, framework: { classPropertyName: "framework", publicName: "framework", isSignal: true, isRequired: false, transformFunction: null }, widgets: { classPropertyName: "widgets", publicName: "widgets", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, JSONSchema: { classPropertyName: "JSONSchema", publicName: "JSONSchema", isSignal: true, isRequired: false, transformFunction: null }, UISchema: { classPropertyName: "UISchema", publicName: "UISchema", isSignal: true, isRequired: false, transformFunction: null }, formData: { classPropertyName: "formData", publicName: "formData", isSignal: true, isRequired: false, transformFunction: null }, ngModel: { classPropertyName: "ngModel", publicName: "ngModel", isSignal: true, isRequired: false, transformFunction: null }, language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null }, loadExternalAssets: { classPropertyName: "loadExternalAssets", publicName: "loadExternalAssets", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, ajvOptions: { classPropertyName: "ajvOptions", publicName: "ajvOptions", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onChanges: "onChanges", onSubmit: "onSubmit", isValid: "isValid", validationErrors: "validationErrors", formSchema: "formSchema", formLayout: "formLayout", dataChange: "dataChange", modelChange: "modelChange", formDataChange: "formDataChange", ngModelChange: "ngModelChange" }, providers: [JsonSchemaFormService, JSON_SCHEMA_FORM_VALUE_ACCESSOR], usesOnChanges: true, ngImport: i0, template: "<form [autocomplete]=\"jsf?.formOptions?.autocomplete ? 'on' : 'off'\" class=\"json-schema-form\" (ngSubmit)=\"submitForm()\">\r\n <root-widget [layout]=\"jsf?.layout\"></root-widget>\r\n</form>\r\n<div *ngIf=\"debug() || jsf?.formOptions?.debug\">\r\n Debug output:\r\n <pre>{{debugOutput}}</pre>\r\n</div>", dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: RootComponent, selector: "root-widget", inputs: ["dataIndex", "layoutIndex", "layout", "isOrderable", "isFlexItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10231
11309
  }
10232
11310
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: JsonSchemaFormComponent, decorators: [{
10233
11311
  type: Component,
@@ -10263,5 +11341,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
10263
11341
  * Generated bundle index. Do not edit.
10264
11342
  */
10265
11343
 
10266
- export { AddReferenceComponent, BASIC_WIDGETS, ButtonComponent, CheckboxComponent, CheckboxesComponent, ElementAttributeDirective, FileComponent, Framework, FrameworkLibraryService, HiddenComponent, InputComponent, JsonPointer, JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonValidators, MessageComponent, NoneComponent, NumberComponent, OneOfComponent, OrderableDirective, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, WidgetLibraryModule, WidgetLibraryService, _executeAsyncValidators, _executeValidators, _mergeErrors, _mergeObjects, _toPromise, addClasses, buildFormGroup, buildFormGroupTemplate, buildLayout, buildLayoutFromSchema, buildSchemaFromData, buildSchemaFromLayout, buildTitleMap, checkInlineType, combineAllOf, commonItems, convertSchemaToDraft6, copy, deValidationMessages, enValidationMessages, esValidationMessages, fixRequiredArrayProperties, fixTitle, forEach, forEachCopy, formatFormData, frValidationMessages, getControl, getControlValidators, getFromSchema, getInputType, getLayoutNode, getSubSchema, getTitleMapFromOneOf, getType, hasOwn, hasValue, inArray, isArray, isBoolean, isDate, isDefined, isEmpty, isFunction, isInputRequired, isInteger, isMap, isNumber, isObject, isObservable, isPrimitive, isPromise, isSet, isString, isType, itValidationMessages, mapLayout, mergeFilteredObject, mergeSchemas, ptValidationMessages, removeRecursiveReferences, resolveSchemaReferences, setRequiredFields, toJavaScriptType, toObservable, toSchemaType, toTitleCase, uniqueItems, updateInputOptions, xor, zhValidationMessages };
11344
+ export { AddReferenceComponent, BASIC_WIDGETS, ButtonComponent, CheckboxComponent, CheckboxesComponent, ElementAttributeDirective, FileComponent, Framework, FrameworkLibraryService, HiddenComponent, InputComponent, JsonPointer, JsonSchemaFormComponent, JsonSchemaFormModule, JsonSchemaFormService, JsonValidators, MessageComponent, NoneComponent, NumberComponent, OneOfComponent, OrderableDirective, RadiosComponent, RootComponent, SectionComponent, SelectComponent, SelectFrameworkComponent, SelectWidgetComponent, SubmitComponent, TabComponent, TabsComponent, TemplateComponent, TextareaComponent, WidgetLibraryModule, WidgetLibraryService, _executeAsyncValidators, _executeValidators, _mergeErrors, _mergeObjects, _toPromise, addClasses, buildFormGroup, buildFormGroupTemplate, buildLayout, buildLayoutFromSchema, buildSchemaFromData, buildSchemaFromLayout, buildTitleMap, checkInlineType, combineAllOf, commonItems, convertSchemaToDraft6, copy, deValidationMessages, enValidationMessages, esValidationMessages, fixRequiredArrayProperties, fixTitle, forEach, forEachCopy, formatFormData, frValidationMessages, getControl, getControlValidators, getFromSchema, getInputType, getLayoutNode, getSubSchema, getTitleMapFromOneOf, getType, hasNonNullValue, hasOwn, hasValue, inArray, isArray, isBoolean, isDate, isDefined, isEmpty, isFunction, isInputRequired, isInteger, isMap, isNumber, isObject, isObservable, isPrimitive, isPromise, isSet, isString, isType, itValidationMessages, mapLayout, mergeFilteredObject, mergeSchemas, path2ControlKey, ptValidationMessages, removeRecursiveReferences, resolveSchemaReferences, setControl, setRequiredFields, toJavaScriptType, toObservable, toSchemaType, toTitleCase, uniqueItems, updateInputOptions, xor, zhValidationMessages };
10267
11345
  //# sourceMappingURL=ng-formworks-core.mjs.map