@ng-formworks/core 18.5.8 → 18.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/esm2022/lib/json-schema-form.component.mjs +22 -6
  2. package/esm2022/lib/json-schema-form.service.mjs +120 -29
  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 +256 -11
  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 +71 -4
  14. package/esm2022/lib/shared/json.validators.mjs +11 -6
  15. package/esm2022/lib/shared/layout.functions.mjs +123 -12
  16. package/esm2022/lib/shared/utility.functions.mjs +31 -1
  17. package/esm2022/lib/widget-library/add-reference.component.mjs +20 -16
  18. package/esm2022/lib/widget-library/button.component.mjs +5 -2
  19. package/esm2022/lib/widget-library/checkbox.component.mjs +5 -2
  20. package/esm2022/lib/widget-library/checkboxes.component.mjs +10 -3
  21. package/esm2022/lib/widget-library/file.component.mjs +5 -2
  22. package/esm2022/lib/widget-library/hidden.component.mjs +5 -2
  23. package/esm2022/lib/widget-library/input.component.mjs +4 -1
  24. package/esm2022/lib/widget-library/number.component.mjs +4 -1
  25. package/esm2022/lib/widget-library/one-of.component.mjs +72 -4
  26. package/esm2022/lib/widget-library/radios.component.mjs +6 -3
  27. package/esm2022/lib/widget-library/root.component.mjs +17 -1
  28. package/esm2022/lib/widget-library/select.component.mjs +92 -7
  29. package/esm2022/lib/widget-library/submit.component.mjs +3 -2
  30. package/esm2022/lib/widget-library/tabs.component.mjs +77 -29
  31. package/esm2022/lib/widget-library/textarea.component.mjs +5 -2
  32. package/esm2022/lib/widget-library/widget-library.module.mjs +15 -3
  33. package/fesm2022/ng-formworks-core.mjs +1069 -230
  34. package/fesm2022/ng-formworks-core.mjs.map +1 -1
  35. package/lib/json-schema-form.component.d.ts +10 -1
  36. package/lib/json-schema-form.service.d.ts +22 -4
  37. package/lib/shared/form-group.functions.d.ts +27 -1
  38. package/lib/shared/format-regex.constants.d.ts +2 -1
  39. package/lib/shared/index.d.ts +6 -6
  40. package/lib/shared/json-schema.functions.d.ts +22 -0
  41. package/lib/shared/json.validators.d.ts +2 -2
  42. package/lib/shared/layout.functions.d.ts +1 -1
  43. package/lib/shared/utility.functions.d.ts +15 -0
  44. package/lib/widget-library/button.component.d.ts +3 -2
  45. package/lib/widget-library/checkbox.component.d.ts +3 -2
  46. package/lib/widget-library/checkboxes.component.d.ts +3 -2
  47. package/lib/widget-library/file.component.d.ts +3 -2
  48. package/lib/widget-library/hidden.component.d.ts +3 -2
  49. package/lib/widget-library/index.d.ts +1 -1
  50. package/lib/widget-library/input.component.d.ts +3 -2
  51. package/lib/widget-library/number.component.d.ts +3 -2
  52. package/lib/widget-library/one-of.component.d.ts +4 -2
  53. package/lib/widget-library/radios.component.d.ts +3 -2
  54. package/lib/widget-library/root.component.d.ts +1 -0
  55. package/lib/widget-library/select.component.d.ts +5 -2
  56. package/lib/widget-library/textarea.component.d.ts +3 -2
  57. 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, 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,40 @@ 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, negate = false) {
4592
+ let conditionFun = "";
4593
+ let condition = {};
4594
+ let notOp = negate ? "!" : "";
4595
+ if (schema.if) {
4596
+ Object.keys(schema.if.properties).forEach((ifProp, ind) => {
4597
+ let amper = ind > 0 ? "&" : "";
4598
+ //Note the model value is first converted to string and so is the condition
4599
+ //so that booleans and numbers can also be compared
4600
+ conditionFun += `${amper}model.${ifProp}+""=='${schema.if.properties[ifProp].const}'`;
4601
+ });
4602
+ }
4603
+ condition["functionBody"] = `return ${notOp}(${conditionFun})`;
4604
+ return condition;
4605
+ }
4489
4606
 
4490
4607
  function convertSchemaToDraft6(schema, options = {}) {
4491
4608
  let draft = options.draft || null;
@@ -4786,6 +4903,17 @@ function convertSchemaToDraft6(schema, options = {}) {
4786
4903
  return newSchema;
4787
4904
  }
4788
4905
 
4906
+ /**
4907
+ * path2ControlKey takes a datapointer path like /some/pointer/path
4908
+ * and returns something like $some$pointer$path
4909
+ * used mainly to convert paths so it can be used as keys in FormGroups
4910
+ * fot ITE scenarios
4911
+ * @param path
4912
+ * @returns string
4913
+ */
4914
+ function path2ControlKey(path) {
4915
+ return path.replace(/\//g, "$");
4916
+ }
4789
4917
  /**
4790
4918
  * FormGroup function library:
4791
4919
  *
@@ -4834,11 +4962,13 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4834
4962
  }
4835
4963
  // TODO: If nodeValue still not set, check layout for default value
4836
4964
  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';
4965
+ const isIfThenElse = hasOwn(schema, 'if') || hasOwn(schema, 'then') || hasOwn(schema, 'else');
4966
+ const controlType = isIfThenElse && !schemaType ? 'IfThenElse' :
4967
+ (hasOwn(schema, 'properties') || hasOwn(schema, 'additionalProperties')) &&
4968
+ schemaType === 'object' ? 'FormGroup' :
4969
+ (hasOwn(schema, 'items') || hasOwn(schema, 'additionalItems')) &&
4970
+ schemaType === 'array' ? 'FormArray' :
4971
+ !schemaType && hasOwn(schema, '$ref') ? '$ref' : 'FormControl';
4842
4972
  const shortDataPointer = removeRecursiveReferences(dataPointer, jsf.dataRecursiveRefMap, jsf.arrayMap);
4843
4973
  if (!jsf.dataMap.has(shortDataPointer)) {
4844
4974
  jsf.dataMap.set(shortDataPointer, new Map());
@@ -4877,8 +5007,135 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4877
5007
  propertyKeys
4878
5008
  .filter(key => hasOwn(schema.properties, key) ||
4879
5009
  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));
5010
+ .forEach(key => {
5011
+ controls[key] = buildFormGroupTemplate(jsf, JsonPointer.get(nodeValue, [key]), setValues, schemaPointer + (hasOwn(schema.properties, key) ?
5012
+ '/properties/' + key : '/additionalProperties'), dataPointer + '/' + key, templatePointer + '/controls/' + key);
5013
+ //add the $<control> type to the root
5014
+ //so it can be flattened and acceses directly in the formgroup
5015
+ //by its full '$' path
5016
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
5017
+ if (controls[key].controls && controls[key].controls[`_${ofType}`]) {
5018
+ Object.keys(controls[key].controls[`_${ofType}`]).forEach($key => {
5019
+ controls[$key] = controls[key].controls[`_${ofType}`][$key];
5020
+ delete controls[key].controls[$key];
5021
+ });
5022
+ delete controls[key].controls[`_${ofType}`];
5023
+ }
5024
+ });
5025
+ });
5026
+ if (hasOwn(schema, "if")) {
5027
+ ["then", "else"].forEach(con => {
5028
+ if (hasOwn(schema, con)) {
5029
+ const keySchemaPointer = `/${con}`;
5030
+ let thenFGTemplate = buildFormGroupTemplate(jsf, nodeValue, false, //JsonPointer.get(nodeValue, keySchemaPointer), setValues,
5031
+ schemaPointer + keySchemaPointer, dataPointer, templatePointer + `/controls/${con}`);
5032
+ Object.assign(controls, thenFGTemplate.controls);
5033
+ }
5034
+ });
5035
+ }
5036
+ /* treat allOf the same as any of but need to add an extra
5037
+ condition for which anyOf item is to be rendered
5038
+ let allOfControls = {}
5039
+ let allOfAllowedKeys = ["allOf", "anyOf", "oneOf", "if", "then", "else", "type", "properties", "items"];
5040
+ if (hasOwn(schema, "allOf") && isArray(schema.allOf)) {
5041
+ schema.allOf.forEach((allOfItem, ind) => {
5042
+ let aoItemKeys = Object.keys(allOfItem);
5043
+ let foundKeys = allOfAllowedKeys.filter(value =>
5044
+ aoItemKeys.includes(value)
5045
+ );
5046
+ if (foundKeys && foundKeys.length > 0) {
5047
+ const keySchemaPointer = `/allOf/${ind}`;
5048
+ //console.log(`found:${keySchemaPointer}`);
5049
+ let allOfFGTemplate = buildFormGroupTemplate(
5050
+ jsf, JsonPointer.get(nodeValue, keySchemaPointer), setValues,
5051
+ schemaPointer + keySchemaPointer,
5052
+ dataPointer,
5053
+ templatePointer + '/controls/' + ind
5054
+ );
5055
+ if (allOfFGTemplate.controls) {
5056
+ Object.keys(allOfFGTemplate.controls).forEach(key => {
5057
+ let controlKey = allOfFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}/${key}`;
5058
+ controlKey = path2ControlKey(controlKey);
5059
+ controls[controlKey] = {
5060
+ key: key,
5061
+ schemaPointer: schemaPointer + keySchemaPointer,
5062
+ controls: allOfFGTemplate.controls[key]
5063
+ }
5064
+ controls[controlKey] = allOfFGTemplate.controls[key];
5065
+ controls[controlKey].key = key;
5066
+ controls[controlKey].schemaPointer = allOfFGTemplate.controls[key].schemaPointer || schemaPointer + keySchemaPointer;
5067
+
5068
+ controls[key] = allOfFGTemplate.controls[key];
5069
+ })
5070
+ }
5071
+ //add ui type items to controls
5072
+ if (allOfItem["type"] || allOfItem["properties"] || allOfItem["items"]) {
5073
+ allOfControls[ind] = allOfFGTemplate
5074
+ }
5075
+
5076
+ }
5077
+
5078
+ })
5079
+ controls["allOf"] = allOfControls;
5080
+ }
5081
+ */
5082
+ let ofAllowedKeys = ["allOf", "anyOf", "oneOf", "if", "then", "else", "type", "properties", "items"];
5083
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
5084
+ if (hasOwn(schema, ofType) && isArray(schema[ofType])) {
5085
+ schema[ofType].forEach((ofItem, ind) => {
5086
+ let aoItemKeys = Object.keys(ofItem);
5087
+ let foundKeys = ofAllowedKeys.filter(value => aoItemKeys.includes(value));
5088
+ if (foundKeys && foundKeys.length > 0) {
5089
+ const keySchemaPointer = `/${ofType}/${ind}`;
5090
+ //console.log(`found:${keySchemaPointer}`);
5091
+ let newNodeValue = JsonPointer.get(nodeValue, keySchemaPointer);
5092
+ if (ofType == "oneOf") {
5093
+ newNodeValue = nodeValue;
5094
+ }
5095
+ let allOfFGTemplate = buildFormGroupTemplate(jsf, newNodeValue, setValues, schemaPointer + keySchemaPointer, dataPointer, templatePointer + '/controls/' + ind);
5096
+ if (allOfFGTemplate.controls) {
5097
+ Object.keys(allOfFGTemplate.controls).forEach(key => {
5098
+ const l2SchemaPointer = hasOwn(schema, 'properties') ?
5099
+ '/properties/' + key : key;
5100
+ let controlKey = allOfFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}${l2SchemaPointer}`;
5101
+ controlKey = path2ControlKey(controlKey);
5102
+ /*
5103
+ controls[controlKey] = {
5104
+ key: key,
5105
+ schemaPointer: `${schemaPointer}${keySchemaPointer}/${key}`,//schemaPointer + keySchemaPointer,
5106
+ controls: allOfFGTemplate.controls[key]
5107
+ }
5108
+ */
5109
+ let controlItem = cloneDeep(allOfFGTemplate.controls[key]);
5110
+ controlItem.key = key;
5111
+ controlItem.schemaPointer = controlItem.schemaPointer || `${schemaPointer}${keySchemaPointer}${l2SchemaPointer}`;
5112
+ controls[controlKey] = controlItem;
5113
+ //need to test if value matches schema,
5114
+ //as the same oneOf item will be assigned to the same value
5115
+ //if key is a $oneOf key then it was inserted at the root of the controls
5116
+ //as form control name will be the full(escaped) path
5117
+ const pointerPath = key.startsWith('$oneOf') ? controlItem.schemaPointer : keySchemaPointer;
5118
+ let oneOfItemSchema = JsonPointer.get(schema, pointerPath);
5119
+ let oneOfItemValue = {};
5120
+ oneOfItemValue[key] = controlItem.value?.value;
5121
+ if (controlItem.value && !jsf.ajv.validate(oneOfItemSchema, oneOfItemValue)) {
5122
+ controlItem.value.value = null;
5123
+ }
5124
+ //controls[controlKey] = controlItem;
5125
+ //allOfFGTemplate.controls[key].schemaPointer ||`${schemaPointer}${keySchemaPointer}/${key}`;
5126
+ //allOfFGTemplate.controls[key].schemaPointer || schemaPointer + keySchemaPointer;
5127
+ controls[key] = cloneDeep(allOfFGTemplate.controls[key]);
5128
+ //add schemacontrol to root
5129
+ //controls[controlKey]=controlItem
5130
+ controls[`_${ofType}`] = controls[`_${ofType}`] || {};
5131
+ controls[`_${ofType}`][controlKey] = controlItem;
5132
+ //allOfFGTemplate.controls[key];
5133
+ });
5134
+ }
5135
+ }
5136
+ });
5137
+ }
5138
+ });
4882
5139
  jsf.formOptions.fieldsRequired = setRequiredFields(schema, controls);
4883
5140
  }
4884
5141
  return { controlType, controls, validators };
@@ -4961,6 +5218,29 @@ function buildFormGroupTemplate(jsf, nodeValue = null, setValues = true, schemaP
4961
5218
  disabled: nodeOptions.get('disabled') || false
4962
5219
  };
4963
5220
  return { controlType, value, validators };
5221
+ //TODO may make an IFThenElse widget or integrate it with the section
5222
+ //widget
5223
+ case 'IfThenElse':
5224
+ controls = {};
5225
+ let conditionType;
5226
+ if (hasOwn(schema, "if")) {
5227
+ ["then", "else"].forEach(con => {
5228
+ if (hasOwn(schema, con)) {
5229
+ const keySchemaPointer = `/${con}`;
5230
+ let thenTFGTemplate = buildFormGroupTemplate(jsf, nodeValue, false, schemaPointer + keySchemaPointer, dataPointer, templatePointer + `/controls/${con}`);
5231
+ //NB same property can be in both then and else
5232
+ //so key must be the unique path to control
5233
+ Object.keys(thenTFGTemplate.controls).forEach(key => {
5234
+ let controlKey = thenTFGTemplate.controls[key].schemaPointer || `${schemaPointer}${keySchemaPointer}/${key}`;
5235
+ controlKey = path2ControlKey(controlKey);
5236
+ let cItem = Object.assign({}, thenTFGTemplate.controls[key]);
5237
+ cItem.schemaPointer = `${schemaPointer}${keySchemaPointer}/${key}`;
5238
+ controls[controlKey] = cItem;
5239
+ });
5240
+ }
5241
+ });
5242
+ }
5243
+ return { controlType, controls, validators };
4964
5244
  default:
4965
5245
  return null;
4966
5246
  }
@@ -4992,7 +5272,23 @@ function buildFormGroup(template) {
4992
5272
  const groupControls = {};
4993
5273
  forEach(template.controls, (controls, key) => {
4994
5274
  const newControl = buildFormGroup(controls);
5275
+ //if (newControl) { groupControls[key] = newControl; }
4995
5276
  if (newControl) {
5277
+ /* experimental idea was to try to be able to switch
5278
+ conditional controls dynamically based on their schema pointer
5279
+ (not datapointer as that only maps to one control)
5280
+ Object.defineProperty(groupControls, key, {
5281
+ get: () => {
5282
+ //console.log(`Accessed control: ${key}`);
5283
+ //add switch logic here
5284
+ return ncontrol;
5285
+ },
5286
+ set:(value)=>{
5287
+ ncontrol=value
5288
+ },
5289
+ enumerable: true
5290
+ })
5291
+ */
4996
5292
  groupControls[key] = newControl;
4997
5293
  }
4998
5294
  });
@@ -5142,6 +5438,9 @@ function formatFormData(formData, dataMap, recursiveRefMap, arrayMap, returnEmpt
5142
5438
  }
5143
5439
  else if (typeof value !== 'object' || isDate(value) ||
5144
5440
  (value === null && returnEmptyFields)) {
5441
+ if (genericPointer.substring(0, 2) == "/$") {
5442
+ return formattedData;
5443
+ }
5145
5444
  console.error('formatFormData error: ' +
5146
5445
  `Schema type not found for form value at ${genericPointer}`);
5147
5446
  console.error('dataMap', dataMap);
@@ -5166,14 +5465,15 @@ function formatFormData(formData, dataMap, recursiveRefMap, arrayMap, returnEmpt
5166
5465
  * // {Pointer} dataPointer - JSON Pointer (string or array)
5167
5466
  * // {boolean = false} returnGroup - If true, return group containing control
5168
5467
  * // {group} - Located value (or null, if no control found)
5468
+ * // {string} schemaPointer - string used for conditional controls coming from schema if/then/else
5169
5469
  */
5170
- function getControl(formGroup, dataPointer, returnGroup = false) {
5470
+ function getControl(formGroup, dataPointer, returnGroup = false, schemaPointer) {
5171
5471
  if (!isObject(formGroup) || !JsonPointer.isJsonPointer(dataPointer)) {
5172
5472
  if (!JsonPointer.isJsonPointer(dataPointer)) {
5173
5473
  // If dataPointer input is not a valid JSON pointer, check to
5174
5474
  // see if it is instead a valid object path, using dot notaion
5175
5475
  if (typeof dataPointer === 'string') {
5176
- const formControl = formGroup.get(dataPointer);
5476
+ const formControl = formGroup.get(path2ControlKey(schemaPointer || "")) || formGroup.get(dataPointer);
5177
5477
  if (formControl) {
5178
5478
  return formControl;
5179
5479
  }
@@ -5193,7 +5493,7 @@ function getControl(formGroup, dataPointer, returnGroup = false) {
5193
5493
  // try using formGroup.get() to return the control
5194
5494
  if (typeof formGroup.get === 'function' &&
5195
5495
  dataPointerArray.every(key => key.indexOf('.') === -1)) {
5196
- const formControl = formGroup.get(dataPointerArray.join('.'));
5496
+ const formControl = formGroup.get(path2ControlKey(schemaPointer || "")) || formGroup.get(dataPointerArray.join('.'));
5197
5497
  if (formControl) {
5198
5498
  return formControl;
5199
5499
  }
@@ -5221,6 +5521,68 @@ function getControl(formGroup, dataPointer, returnGroup = false) {
5221
5521
  }
5222
5522
  return subGroup;
5223
5523
  }
5524
+ /**
5525
+ * 'setControl' function
5526
+ *
5527
+ * Uses a JSON Pointer for a data object to retrieve a control from
5528
+ * an Angular formGroup or formGroup template. (Note: though a formGroup
5529
+ * template is much simpler, its basic structure is idential to a formGroup).
5530
+ *
5531
+ * If the optional third parameter 'returnGroup' is set to TRUE, the group
5532
+ * containing the control is returned, rather than the control itself.
5533
+ *
5534
+ * // {FormGroup} formGroup - Angular FormGroup to get value from
5535
+ * // {Pointer} dataPointer - JSON Pointer (string or array)
5536
+ * // {AbstractControl} control - control used to replace existing or add
5537
+ * // {targetKey} - optional string used as the new key-not implemented as yet
5538
+ */
5539
+ function setControl(formGroup, dataPointer, control, targetKey) {
5540
+ let dataPointerArray = JsonPointer.parse(dataPointer);
5541
+ // If formGroup input is a real formGroup (not a formGroup template)
5542
+ // try using formGroup.get() to return the control
5543
+ /*
5544
+ if (typeof formGroup.get === 'function' &&
5545
+ dataPointerArray.every(key => key.indexOf('.') === -1)
5546
+ ) {
5547
+ formGroup.setControl(dataPointerArray.join('.'), control);
5548
+ return;
5549
+ }
5550
+ */
5551
+ let currentGroup = formGroup;
5552
+ for (let i = 0; i < dataPointerArray.length - 1; i++) {
5553
+ // Navigate down the form structure to find the correct nested FormGroup
5554
+ currentGroup = currentGroup.get(dataPointerArray[i]);
5555
+ // If it's not a FormGroup, we throw an error since we can't set a control in a non-group.
5556
+ if (!(typeof currentGroup.setControl === 'function')) {
5557
+ throw new Error(`Path '${dataPointerArray[i]}' is not a valid FormGroup or FormArray.`);
5558
+ }
5559
+ }
5560
+ // Now we are at the parent FormGroup, set the control at the last part of the path
5561
+ const lastPart = dataPointerArray[dataPointerArray.length - 1];
5562
+ // Set the control at the final path (like 'name' inside 'state')
5563
+ currentGroup.setControl(lastPart, control);
5564
+ // If formGroup input is a formGroup template,
5565
+ // or formGroup.get() failed to return the control,
5566
+ // search the formGroup object for dataPointer's control
5567
+ //TODO needs to be adapted to setControl
5568
+ /*
5569
+ let subGroup = formGroup;
5570
+ for (const key of dataPointerArray) {
5571
+ if (hasOwn(subGroup, 'controls')) { subGroup = subGroup.controls; }
5572
+ if (isArray(subGroup) && (key === '-')) {
5573
+ subGroup = subGroup[subGroup.length - 1];
5574
+ } else if (hasOwn(subGroup, key)) {
5575
+ subGroup = subGroup[key];
5576
+ } else {
5577
+ console.error(`getControl error: Unable to find "${key}" item in FormGroup.`);
5578
+ console.error(dataPointer);
5579
+ console.error(formGroup);
5580
+ return;
5581
+ }
5582
+ }
5583
+ return subGroup;
5584
+ */
5585
+ }
5224
5586
 
5225
5587
  /**
5226
5588
  * Layout function library:
@@ -5837,16 +6199,19 @@ function fixNestedArrayLayout(options) {
5837
6199
  * // { string = '' } dataPointerPrefix -
5838
6200
  * //
5839
6201
  */
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);
6202
+ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPointer = '', dataPointer = '', arrayItem = false, arrayItemType = null, removable = null, forRefLibrary = false, dataPointerPrefix = '', jsonSchema) {
6203
+ const jsSchema = jsonSchema || jsf.schema;
6204
+ const schema = JsonPointer.get(jsSchema, schemaPointer);
6205
+ //JsonPointer.get(jsf.schema, schemaPointer);
5842
6206
  if (!hasOwn(schema, 'type') && !hasOwn(schema, '$ref') &&
5843
- !hasOwn(schema, 'x-schema-form')) {
6207
+ !hasOwn(schema, 'x-schema-form')
6208
+ && !hasOwn(schema, 'if') && !hasOwn(schema, 'then') && !hasOwn(schema, 'else')) {
5844
6209
  return null;
5845
6210
  }
5846
6211
  const newNodeType = getInputType(schema);
5847
6212
  if (!isDefined(nodeValue) && (jsf.formOptions.setSchemaDefaults === true ||
5848
6213
  (jsf.formOptions.setSchemaDefaults === 'auto' && isEmpty(jsf.formValues)))) {
5849
- nodeValue = JsonPointer.get(jsf.schema, schemaPointer + '/default');
6214
+ nodeValue = JsonPointer.get(jsSchema, schemaPointer + '/default');
5850
6215
  }
5851
6216
  let newNode = {
5852
6217
  _id: forRefLibrary ? null : uniqueId(),
@@ -5854,7 +6219,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5854
6219
  dataPointer: JsonPointer.toGenericPointer(dataPointer, jsf.arrayMap),
5855
6220
  dataType: schema.type || (hasOwn(schema, '$ref') ? '$ref' : null),
5856
6221
  options: {},
5857
- required: isInputRequired(jsf.schema, schemaPointer),
6222
+ required: isInputRequired(jsSchema, schemaPointer),
5858
6223
  type: newNodeType,
5859
6224
  widget: widgetLibrary.getWidget(newNodeType),
5860
6225
  };
@@ -5914,6 +6279,89 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5914
6279
  newSection.push(innerItem);
5915
6280
  }
5916
6281
  });
6282
+ //treat allOf the same as any of but need to add an extra
6283
+ //condition for which anyOf item is to be rendered
6284
+ ["allOf", "anyOf", "oneOf"].forEach(ofType => {
6285
+ if (hasOwn(schema, ofType) && isArray(schema[ofType])) {
6286
+ let outerOneOfItem;
6287
+ if (ofType == "oneOf") {
6288
+ outerOneOfItem = buildLayoutFromSchema(jsf, widgetLibrary, schema.oneOf, //{type:"tabarray",items:schema.oneOf},
6289
+ "/", //schemaPointer + `/${ofType}`,
6290
+ dataPointer, false, null, null, forRefLibrary, dataPointerPrefix,
6291
+ //{type:"tabarray",items:schema.oneOf,oneOf:schema.oneOf}
6292
+ { type: "one-of", items: schema.oneOf, oneOf: schema.oneOf });
6293
+ //outerItem.items=cloneDeep(newSection);
6294
+ //newSection.length=0;
6295
+ newSection.push(outerOneOfItem);
6296
+ }
6297
+ schema[ofType].forEach((ofItem, ind) => {
6298
+ const keySchemaPointer = `/${ofType}/${ind}`;
6299
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, ofItem, schemaPointer + keySchemaPointer, dataPointer, false, null, null, forRefLibrary, dataPointerPrefix);
6300
+ if (innerItem) {
6301
+ //newSection.push(innerItem);
6302
+ if (innerItem.items) {
6303
+ innerItem.items.forEach(innerItemLevel2 => {
6304
+ const l2SchemaPointer = hasOwn(ofItem, 'properties') ?
6305
+ '/properties/' + innerItemLevel2.name : innerItemLevel2.name;
6306
+ innerItemLevel2.oneOfPointer = schemaPointer + keySchemaPointer + l2SchemaPointer;
6307
+ innerItemLevel2.schemaPointer = innerItemLevel2.oneOfPointer;
6308
+ });
6309
+ }
6310
+ if (isArray(innerItem)) {
6311
+ innerItem.forEach(item => {
6312
+ const l2SchemaPointer = hasOwn(ofItem, 'properties') ?
6313
+ '/properties/' + item.name : item.name;
6314
+ if (outerOneOfItem) {
6315
+ item.oneOfPointer = schemaPointer + keySchemaPointer + l2SchemaPointer;
6316
+ //schemaPointer + keySchemaPointer + item.dataPointer;
6317
+ item.schemaPointer = item.oneOfPointer;
6318
+ outerOneOfItem.items = outerOneOfItem.items || [];
6319
+ outerOneOfItem.items.push(item);
6320
+ }
6321
+ else {
6322
+ newSection.push(item);
6323
+ }
6324
+ });
6325
+ //TODO test-might not work for more than 2 levels of nesting
6326
+ }
6327
+ else {
6328
+ if (outerOneOfItem) {
6329
+ innerItem.oneOfPointer = schemaPointer + keySchemaPointer; // + innerItem.dataPointer;
6330
+ innerItem.schemaPointer = innerItem.oneOfPointer;
6331
+ outerOneOfItem.items = outerOneOfItem.items || [];
6332
+ outerOneOfItem.items.push(innerItem);
6333
+ }
6334
+ else {
6335
+ newSection.push(innerItem);
6336
+ }
6337
+ }
6338
+ }
6339
+ });
6340
+ }
6341
+ });
6342
+ if (hasOwn(schema, "if")) {
6343
+ ["then", "else"].forEach(con => {
6344
+ if (hasOwn(schema, con)) {
6345
+ const keySchemaPointer = `/${con}`;
6346
+ const negateClause = con == "else";
6347
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, nodeValue.then, schemaPointer + keySchemaPointer, dataPointer, false, null, null, forRefLibrary, dataPointerPrefix);
6348
+ if (innerItem) {
6349
+ if (isArray(innerItem)) {
6350
+ innerItem.forEach(item => {
6351
+ item.schemaPointer = schemaPointer + keySchemaPointer + item.dataPointer;
6352
+ item.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6353
+ newSection.push(item);
6354
+ });
6355
+ }
6356
+ else {
6357
+ innerItem.schemaPointer = schemaPointer + keySchemaPointer + innerItem.dataPointer;
6358
+ innerItem.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6359
+ newSection.push(innerItem);
6360
+ }
6361
+ }
6362
+ }
6363
+ });
6364
+ }
5917
6365
  if (dataPointer === '' && !forRefLibrary) {
5918
6366
  newNode = newSection;
5919
6367
  }
@@ -5930,7 +6378,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
5930
6378
  newNode.items = [];
5931
6379
  newNode.options.maxItems = Math.min(schema.maxItems || 1000, newNode.options.maxItems || 1000);
5932
6380
  newNode.options.minItems = Math.max(schema.minItems || 0, newNode.options.minItems || 0);
5933
- if (!newNode.options.minItems && isInputRequired(jsf.schema, schemaPointer)) {
6381
+ if (!newNode.options.minItems && isInputRequired(jsSchema, schemaPointer)) {
5934
6382
  newNode.options.minItems = 1;
5935
6383
  }
5936
6384
  if (!hasOwn(newNode.options, 'listItems')) {
@@ -6063,7 +6511,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6063
6511
  }
6064
6512
  else if (newNode.dataType === '$ref') {
6065
6513
  const schemaRef = JsonPointer.compile(schema.$ref);
6066
- const dataRef = JsonPointer.toDataPointer(schemaRef, jsf.schema);
6514
+ const dataRef = JsonPointer.toDataPointer(schemaRef, jsSchema);
6067
6515
  let buttonText = '';
6068
6516
  // Get newNode title
6069
6517
  if (newNode.options.add) {
@@ -6075,7 +6523,7 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6075
6523
  // If newNode doesn't have a title, look for title of parent array item
6076
6524
  }
6077
6525
  else {
6078
- const parentSchema = JsonPointer.get(jsf.schema, schemaPointer, 0, -1);
6526
+ const parentSchema = JsonPointer.get(jsSchema, schemaPointer, 0, -1);
6079
6527
  if (hasOwn(parentSchema, 'title')) {
6080
6528
  buttonText = 'Add to ' + parentSchema.title;
6081
6529
  }
@@ -6093,9 +6541,9 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6093
6541
  removable: false,
6094
6542
  title: buttonText,
6095
6543
  });
6096
- if (isNumber(JsonPointer.get(jsf.schema, schemaPointer, 0, -1).maxItems)) {
6544
+ if (isNumber(JsonPointer.get(jsSchema, schemaPointer, 0, -1).maxItems)) {
6097
6545
  newNode.options.maxItems =
6098
- JsonPointer.get(jsf.schema, schemaPointer, 0, -1).maxItems;
6546
+ JsonPointer.get(jsSchema, schemaPointer, 0, -1).maxItems;
6099
6547
  }
6100
6548
  // Add layout template to layoutRefLibrary
6101
6549
  if (dataRef.length) {
@@ -6116,6 +6564,31 @@ function buildLayoutFromSchema(jsf, widgetLibrary, nodeValue = null, schemaPoint
6116
6564
  }
6117
6565
  }
6118
6566
  }
6567
+ else if (newNode.type === 'if') {
6568
+ const newSection = [];
6569
+ ["then", "else"].forEach(con => {
6570
+ if (hasOwn(schema, con)) {
6571
+ const keySchemaPointer = `/${con}`;
6572
+ const negateClause = con == "else";
6573
+ const innerItem = buildLayoutFromSchema(jsf, widgetLibrary, nodeValue.then, schemaPointer + keySchemaPointer, dataPointer, false, null, null, forRefLibrary, dataPointerPrefix);
6574
+ if (innerItem) {
6575
+ if (isArray(innerItem)) {
6576
+ innerItem.forEach(item => {
6577
+ item.schemaPointer = schemaPointer + keySchemaPointer + item.dataPointer;
6578
+ item.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6579
+ newSection.push(item);
6580
+ newNode = newSection;
6581
+ });
6582
+ }
6583
+ else {
6584
+ innerItem.schemaPointer = schemaPointer + keySchemaPointer + innerItem.dataPointer;
6585
+ innerItem.options.condition = convertJSONSchemaIfToCondition(schema, negateClause);
6586
+ newNode = innerItem;
6587
+ }
6588
+ }
6589
+ }
6590
+ });
6591
+ }
6119
6592
  return newNode;
6120
6593
  }
6121
6594
  /**
@@ -6382,6 +6855,32 @@ class JsonSchemaFormService {
6382
6855
  setSortableOptions(value) {
6383
6856
  this.sortableOptionsSubject.next(value); // Update the sortable options value
6384
6857
  }
6858
+ createAjvInstance(ajvOptions) {
6859
+ let ajvInstance = new Ajv2019(ajvOptions);
6860
+ ajvInstance.addMetaSchema(jsonDraft6);
6861
+ ajvInstance.addMetaSchema(jsonDraft7);
6862
+ addFormats(ajvInstance);
6863
+ return ajvInstance;
6864
+ }
6865
+ createAndRegisterAjvInstance(ajvOptions, name) {
6866
+ const intanceName = name || `ajv_${Date.now()}`;
6867
+ if (this.ajvRegistry[intanceName]) {
6868
+ throw new Error(`ajv instance with name:'${intanceName}' has already been registered`);
6869
+ }
6870
+ const ajvInstance = this.createAjvInstance(ajvOptions);
6871
+ this.ajvRegistry[intanceName] = {
6872
+ name: intanceName,
6873
+ ajvInstance: ajvInstance,
6874
+ ajvValidator: null
6875
+ };
6876
+ return this.ajvRegistry[intanceName];
6877
+ }
6878
+ getAjvInstance(name = 'default') {
6879
+ return this.ajvRegistry[name].ajvInstance;
6880
+ }
6881
+ getAjvValidator(name = 'default') {
6882
+ return this.ajvRegistry[name]?.ajvValidator;
6883
+ }
6385
6884
  constructor() {
6386
6885
  this.JsonFormCompatibility = false;
6387
6886
  this.ReactJsonSchemaFormCompatibility = false;
@@ -6389,10 +6888,11 @@ class JsonSchemaFormService {
6389
6888
  this.tpldata = {};
6390
6889
  this.ajvOptions = {
6391
6890
  allErrors: true,
6392
- validateFormats: false,
6891
+ //validateFormats:false,
6393
6892
  strict: false
6394
6893
  };
6395
6894
  this.ajv = new Ajv2019(this.ajvOptions); // AJV: Another JSON Schema Validator
6895
+ //Being replaced by getAjvValidator()
6396
6896
  this.validateFormData = null; // Compiled AJV function to validate active form's schema
6397
6897
  this.formValues = {}; // Internal form data (may not have correct types)
6398
6898
  this.data = {}; // Output form data (formValues, formatted with correct data types)
@@ -6466,6 +6966,7 @@ class JsonSchemaFormService {
6466
6966
  validationMessages: {} // set by setLanguage()
6467
6967
  }
6468
6968
  };
6969
+ //TODO-review,may not be needed as sortablejs replaces dnd
6469
6970
  //this has been added to enable or disable the dragabble state of a component
6470
6971
  //using the OrderableDirective, mainly when an <input type="range">
6471
6972
  //elements are present, as the draggable attribute makes it difficult to
@@ -6478,9 +6979,19 @@ class JsonSchemaFormService {
6478
6979
  //nxt-sortablejs and sortablejs
6479
6980
  this.sortableOptionsSubject = new BehaviorSubject({ disabled: false }); // Default value true
6480
6981
  this.sortableOptions$ = this.sortableOptionsSubject.asObservable();
6982
+ this.ajvRegistry = {};
6481
6983
  this.setLanguage(this.language);
6482
6984
  this.ajv.addMetaSchema(jsonDraft6);
6483
6985
  this.ajv.addMetaSchema(jsonDraft7);
6986
+ addFormats(this.ajv);
6987
+ this.ajvRegistry['default'] = { name: 'default', ajvInstance: this.ajv, ajvValidator: null };
6988
+ // Add custom 'duration' format using a regex
6989
+ /*
6990
+ this.ajv.addFormat("duration", {
6991
+ type: "string",
6992
+ validate: (duration) => /^P(?!$)(\d+Y)?(\d+M)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?$/.test(duration)
6993
+ });
6994
+ */
6484
6995
  }
6485
6996
  ngOnDestroy() {
6486
6997
  this.fcValueChangesSubs?.unsubscribe();
@@ -6519,7 +7030,7 @@ class JsonSchemaFormService {
6519
7030
  this.ReactJsonSchemaFormCompatibility = false;
6520
7031
  this.AngularSchemaFormCompatibility = false;
6521
7032
  this.tpldata = {};
6522
- this.validateFormData = null;
7033
+ this.validateFormData = null; //Being replaced by getAjvValidator()
6523
7034
  this.formValues = {};
6524
7035
  this.schema = {};
6525
7036
  this.layout = [];
@@ -6538,6 +7049,8 @@ class JsonSchemaFormService {
6538
7049
  this.schemaRefLibrary = {};
6539
7050
  this.templateRefLibrary = {};
6540
7051
  this.formOptions = cloneDeep(this.defaultFormOptions);
7052
+ this.ajvRegistry = {};
7053
+ this.ajvRegistry['default'] = { name: 'default', ajvInstance: this.ajv, ajvValidator: null };
6541
7054
  }
6542
7055
  /**
6543
7056
  * 'buildRemoteError' function
@@ -6569,10 +7082,10 @@ class JsonSchemaFormService {
6569
7082
  }
6570
7083
  });
6571
7084
  }
6572
- validateData(newValue, updateSubscriptions = true) {
7085
+ validateData(newValue, updateSubscriptions = true, ajvInstanceName = 'default') {
6573
7086
  // Format raw form data to correct data types
6574
7087
  this.data = formatFormData(newValue, this.dataMap, this.dataRecursiveRefMap, this.arrayMap, this.formOptions.returnEmptyFields);
6575
- this.isValid = this.validateFormData(this.data);
7088
+ this.isValid = this.getAjvValidator(ajvInstanceName)(this.data);
6576
7089
  this.validData = this.isValid ? this.data : null;
6577
7090
  const compileErrors = (errors) => {
6578
7091
  const compiledErrors = {};
@@ -6587,8 +7100,9 @@ class JsonSchemaFormService {
6587
7100
  });
6588
7101
  return compiledErrors;
6589
7102
  };
6590
- this.ajvErrors = this.validateFormData.errors;
6591
- this.validationErrors = compileErrors(this.validateFormData.errors);
7103
+ //TODO:store avjErrors per ajvInstance in registry
7104
+ this.ajvErrors = this.getAjvValidator(ajvInstanceName).errors;
7105
+ this.validationErrors = compileErrors(this.ajvErrors);
6592
7106
  if (updateSubscriptions) {
6593
7107
  this.dataChanges.next(this.data);
6594
7108
  this.isValidChanges.next(this.isValid);
@@ -6598,16 +7112,16 @@ class JsonSchemaFormService {
6598
7112
  buildFormGroupTemplate(formValues = null, setValues = true) {
6599
7113
  this.formGroupTemplate = buildFormGroupTemplate(this, formValues, setValues);
6600
7114
  }
6601
- buildFormGroup() {
7115
+ buildFormGroup(ajvInstanceName) {
6602
7116
  this.formGroup = buildFormGroup(this.formGroupTemplate);
6603
7117
  if (this.formGroup) {
6604
- this.compileAjvSchema();
6605
- this.validateData(this.formGroup.value);
7118
+ this.compileAjvSchema(ajvInstanceName);
7119
+ this.validateData(this.formGroup.value, true, ajvInstanceName);
6606
7120
  // Set up observables to emit data and validation info when form data changes
6607
7121
  if (this.formValueSubscription) {
6608
7122
  this.formValueSubscription.unsubscribe();
6609
7123
  }
6610
- this.formValueSubscription = this.formGroup.valueChanges.subscribe(formValue => this.validateData(formValue));
7124
+ this.formValueSubscription = this.formGroup.valueChanges.subscribe(formValue => this.validateData(formValue, true, ajvInstanceName));
6611
7125
  }
6612
7126
  }
6613
7127
  buildLayout(widgetLibrary) {
@@ -6636,15 +7150,17 @@ class JsonSchemaFormService {
6636
7150
  });
6637
7151
  }
6638
7152
  }
6639
- compileAjvSchema() {
6640
- if (!this.validateFormData) {
7153
+ compileAjvSchema(ajvInstanceName = 'default') {
7154
+ let ajvValidator = this.getAjvValidator(ajvInstanceName);
7155
+ if (!ajvValidator) {
6641
7156
  // if 'ui:order' exists in properties, move it to root before compiling with ajv
6642
7157
  if (Array.isArray(this.schema.properties['ui:order'])) {
6643
7158
  this.schema['ui:order'] = this.schema.properties['ui:order'];
6644
7159
  delete this.schema.properties['ui:order'];
6645
7160
  }
6646
- this.ajv.removeSchema(this.schema);
6647
- this.validateFormData = this.ajv.compile(this.schema);
7161
+ this.getAjvInstance(ajvInstanceName).removeSchema(this.schema);
7162
+ ajvValidator = this.getAjvInstance(ajvInstanceName).compile(this.schema);
7163
+ this.ajvRegistry[ajvInstanceName].ajvValidator = ajvValidator;
6648
7164
  }
6649
7165
  }
6650
7166
  buildSchemaFromData(data, requireAllFields = false) {
@@ -6806,12 +7322,23 @@ class JsonSchemaFormService {
6806
7322
  if (!isObject(ctx)) {
6807
7323
  return false;
6808
7324
  }
7325
+ const layoutNode = ctx.layoutNode();
6809
7326
  if (isEmpty(ctx.options)) {
6810
- ctx.options = !isEmpty((ctx.layoutNode() || {}).options)
6811
- ? ctx.layoutNode().options
7327
+ ctx.options = !isEmpty((layoutNode || {}).options)
7328
+ ? layoutNode.options
6812
7329
  : cloneDeep(this.formOptions);
6813
7330
  }
6814
7331
  ctx.formControl = this.getFormControl(ctx);
7332
+ //introduced to check if the node is part of ITE conditional
7333
+ //then change or add the control
7334
+ if (layoutNode?.schemaPointer) {
7335
+ //before changing control, need to set the new data type for data formatting
7336
+ const schemaType = this.dataMap.get(layoutNode?.dataPointer).get("schemaType");
7337
+ if (schemaType != layoutNode.dataType) {
7338
+ this.dataMap.get(layoutNode?.dataPointer).set("schemaType", layoutNode.dataType);
7339
+ }
7340
+ this.setFormControl(ctx, ctx.formControl);
7341
+ }
6815
7342
  ctx.boundControl = bind && !!ctx.formControl;
6816
7343
  if (ctx.formControl) {
6817
7344
  ctx.controlName = this.getFormControlName(ctx);
@@ -6830,27 +7357,27 @@ class JsonSchemaFormService {
6830
7357
  ? null
6831
7358
  : this.formatErrors(ctx.formControl.errors, ctx.options.validationMessages)));
6832
7359
  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
7360
  if (!isEqual$1(ctx.controlValue, value)) {
6842
7361
  ctx.controlValue = value;
6843
7362
  }
6844
7363
  });
6845
7364
  }
6846
7365
  else {
6847
- ctx.controlName = ctx.layoutNode().name;
6848
- ctx.controlValue = ctx.layoutNode().value || null;
7366
+ ctx.controlName = layoutNode.name;
7367
+ ctx.controlValue = layoutNode.value || null;
6849
7368
  const dataPointer = this.getDataPointer(ctx);
6850
7369
  if (bind && dataPointer) {
6851
7370
  console.error(`warning: control "${dataPointer}" is not bound to the Angular FormGroup.`);
6852
7371
  }
6853
7372
  }
7373
+ //if this is a ITE conditional field, the value would not have been
7374
+ //set, as the control would only be initialized when the condition is true
7375
+ if (ctx.options?.condition) {
7376
+ const value = JsonPointer.has(this.formValues, layoutNode.dataPointer)
7377
+ ? JsonPointer.get(this.formValues, layoutNode.dataPointer)
7378
+ : ctx.options?.default;
7379
+ ctx.formControl?.setValue(value);
7380
+ }
6854
7381
  return ctx.boundControl;
6855
7382
  }
6856
7383
  formatErrors(errors, validationMessages = {}) {
@@ -6932,13 +7459,48 @@ class JsonSchemaFormService {
6932
7459
  }
6933
7460
  formArray.markAsDirty();
6934
7461
  }
7462
+ updateArrayMultiSelectList(ctx, selectList) {
7463
+ this.updateArrayCheckboxList(ctx, selectList);
7464
+ /* const formArray = <UntypedFormArray>this.getFormControl(ctx);
7465
+
7466
+ // Remove all existing items
7467
+ while (formArray.value.length) {
7468
+ formArray.removeAt(0);
7469
+ }
7470
+
7471
+ // Re-add an item for each checked box
7472
+ const refPointer = removeRecursiveReferences(
7473
+ ctx.layoutNode().dataPointer + '/-',
7474
+ this.dataRecursiveRefMap,
7475
+ this.arrayMap
7476
+ );
7477
+ for (const selectItem of selectList) {
7478
+ if (selectItem.value) {
7479
+ const newFormControl = buildFormGroup(
7480
+ this.templateRefLibrary[refPointer]
7481
+ );
7482
+ newFormControl.setValue(selectItem.value);
7483
+ formArray.push(newFormControl);
7484
+ }
7485
+ }
7486
+ formArray.markAsDirty();
7487
+ */
7488
+ }
6935
7489
  getFormControl(ctx) {
6936
7490
  if (!ctx || !ctx.layoutNode ||
6937
7491
  !isDefined(ctx.layoutNode().dataPointer) ||
6938
7492
  ctx.layoutNode().type === '$ref') {
6939
7493
  return null;
6940
7494
  }
6941
- return getControl(this.formGroup, this.getDataPointer(ctx));
7495
+ return getControl(this.formGroup, this.getDataPointer(ctx), false, ctx.layoutNode()?.schemaPointer);
7496
+ }
7497
+ setFormControl(ctx, control) {
7498
+ if (!ctx || !ctx.layoutNode ||
7499
+ !isDefined(ctx.layoutNode().dataPointer) ||
7500
+ ctx.layoutNode().type === '$ref') {
7501
+ return null;
7502
+ }
7503
+ return setControl(this.formGroup, this.getDataPointer(ctx), control);
6942
7504
  }
6943
7505
  getFormControlValue(ctx) {
6944
7506
  if (!ctx || !ctx.layoutNode ||
@@ -6953,7 +7515,7 @@ class JsonSchemaFormService {
6953
7515
  if (!ctx || !ctx.layoutNode || !isDefined(ctx.layoutNode().dataPointer)) {
6954
7516
  return null;
6955
7517
  }
6956
- return getControl(this.formGroup, this.getDataPointer(ctx), true);
7518
+ return getControl(this.formGroup, this.getDataPointer(ctx), true, ctx.layoutNode()?.schemaPointer);
6957
7519
  }
6958
7520
  getFormControlName(ctx) {
6959
7521
  if (!ctx || !ctx.layoutNode ||
@@ -7206,13 +7768,15 @@ class AddReferenceComponent {
7206
7768
  }
7207
7769
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddReferenceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7208
7770
  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 }); }
7771
+ <section [class]="options?.htmlClass || ''" align="end">
7772
+ <button *ngIf="showAddButton"
7773
+ [class]="options?.fieldHtmlClass || ''" class="sortable-filter sortable-fixed"
7774
+ [disabled]="options?.readonly"
7775
+ (click)="addItem($event)">
7776
+ <span *ngIf="options?.icon" [class]="options?.icon"></span>
7777
+ <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7778
+ </button>
7779
+ </section>`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); }
7216
7780
  }
7217
7781
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AddReferenceComponent, decorators: [{
7218
7782
  type: Component,
@@ -7220,13 +7784,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7220
7784
  // tslint:disable-next-line:component-selector
7221
7785
  selector: 'add-reference-widget',
7222
7786
  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>`,
7787
+ <section [class]="options?.htmlClass || ''" align="end">
7788
+ <button *ngIf="showAddButton"
7789
+ [class]="options?.fieldHtmlClass || ''" class="sortable-filter sortable-fixed"
7790
+ [disabled]="options?.readonly"
7791
+ (click)="addItem($event)">
7792
+ <span *ngIf="options?.icon" [class]="options?.icon"></span>
7793
+ <span *ngIf="options?.title" [innerHTML]="buttonText"></span>
7794
+ </button>
7795
+ </section>`,
7230
7796
  changeDetection: ChangeDetectionStrategy.Default,
7231
7797
  }]
7232
7798
  }] });
@@ -7252,6 +7818,9 @@ class ButtonComponent {
7252
7818
  this.jsf.updateValue(this, event.target.value);
7253
7819
  }
7254
7820
  }
7821
+ ngOnDestroy() {
7822
+ this.jsf.updateValue(this, null);
7823
+ }
7255
7824
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7256
7825
  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
7826
  <div
@@ -7321,6 +7890,9 @@ class CheckboxComponent {
7321
7890
  get isChecked() {
7322
7891
  return this.jsf.getFormControlValue(this) === this.trueValue;
7323
7892
  }
7893
+ ngOnDestroy() {
7894
+ this.jsf.updateValue(this, null);
7895
+ }
7324
7896
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7325
7897
  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
7898
  <label
@@ -7425,6 +7997,13 @@ class CheckboxesComponent {
7425
7997
  this.jsf.updateArrayCheckboxList(this, this.checkboxList);
7426
7998
  }
7427
7999
  }
8000
+ //TODO review this
8001
+ ngOnDestroy() {
8002
+ //this.jsf.updateValue(this, null);
8003
+ let nullVal = [];
8004
+ this.formControl.reset(nullVal);
8005
+ this.controlValue = null;
8006
+ }
7428
8007
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CheckboxesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7429
8008
  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
8009
  <label *ngIf="options?.title"
@@ -7550,6 +8129,9 @@ class FileComponent {
7550
8129
  updateValue(event) {
7551
8130
  this.jsf.updateValue(this, event.target.value);
7552
8131
  }
8132
+ ngOnDestroy() {
8133
+ this.jsf.updateValue(this, null);
8134
+ }
7553
8135
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7554
8136
  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
8137
  }
@@ -7574,6 +8156,9 @@ class HiddenComponent {
7574
8156
  ngOnInit() {
7575
8157
  this.jsf.initializeControl(this);
7576
8158
  }
8159
+ ngOnDestroy() {
8160
+ this.jsf.updateValue(this, null);
8161
+ }
7577
8162
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HiddenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7578
8163
  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
8164
  <input *ngIf="boundControl"
@@ -7629,6 +8214,9 @@ class InputComponent {
7629
8214
  updateValue(event) {
7630
8215
  this.jsf.updateValue(this, event.target.value);
7631
8216
  }
8217
+ ngOnDestroy() {
8218
+ this.jsf.updateValue(this, null);
8219
+ }
7632
8220
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7633
8221
  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
8222
  <div [class]="options?.htmlClass || ''" class="sortable-filter" >
@@ -7808,6 +8396,9 @@ class NumberComponent {
7808
8396
  updateValue(event) {
7809
8397
  this.jsf.updateValue(this, event.target.value);
7810
8398
  }
8399
+ ngOnDestroy() {
8400
+ this.jsf.updateValue(this, null);
8401
+ }
7811
8402
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: NumberComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7812
8403
  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
8404
  <div #divElt [class]="options?.htmlClass || ''" class="sortable-filter" >
@@ -7916,6 +8507,194 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7916
8507
  args: ['divElt', {}]
7917
8508
  }] } });
7918
8509
 
8510
+ class SelectFrameworkComponent {
8511
+ constructor() {
8512
+ this.jsf = inject(JsonSchemaFormService);
8513
+ this.newComponent = null;
8514
+ this.layoutNode = input(undefined);
8515
+ this.layoutIndex = input(undefined);
8516
+ this.dataIndex = input(undefined);
8517
+ this.widgetContainer = viewChild('widgetContainer', { read: ViewContainerRef });
8518
+ }
8519
+ ngOnInit() {
8520
+ this.updateComponent();
8521
+ }
8522
+ ngOnChanges() {
8523
+ this.updateComponent();
8524
+ }
8525
+ updateComponent() {
8526
+ const widgetContainer = this.widgetContainer();
8527
+ if (widgetContainer && !this.newComponent && this.jsf.framework) {
8528
+ this.newComponent = widgetContainer.createComponent((this.jsf.framework));
8529
+ //TODO fix all deprecated calls and test
8530
+ //this.widgetContainer.createComponent<any>(this.jsf.framework)
8531
+ }
8532
+ if (this.newComponent) {
8533
+ for (const input of ['layoutNode', 'layoutIndex', 'dataIndex']) {
8534
+ this.newComponent.instance[input] = this[input];
8535
+ }
8536
+ }
8537
+ }
8538
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectFrameworkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8539
+ 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 }); }
8540
+ }
8541
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectFrameworkComponent, decorators: [{
8542
+ type: Component,
8543
+ args: [{
8544
+ // tslint:disable-next-line:component-selector
8545
+ selector: 'select-framework-widget',
8546
+ template: `<div #widgetContainer></div>`,
8547
+ }]
8548
+ }] });
8549
+
8550
+ class TabsComponent {
8551
+ constructor() {
8552
+ this.jsf = inject(JsonSchemaFormService);
8553
+ this.selectedItem = 0;
8554
+ this.showAddTab = true;
8555
+ this.layoutNode = input(undefined);
8556
+ this.layoutIndex = input(undefined);
8557
+ this.dataIndex = input(undefined);
8558
+ }
8559
+ ngOnInit() {
8560
+ this.options = this.layoutNode().options || {};
8561
+ if (this.options.selectedTab) {
8562
+ this.selectedItem = this.options.selectedTab;
8563
+ }
8564
+ this.itemCount = this.layoutNode().items.length - 1;
8565
+ this.updateControl();
8566
+ }
8567
+ select(index) {
8568
+ const layoutNode = this.layoutNode();
8569
+ if (layoutNode.items[index].type === '$ref') {
8570
+ this.itemCount = layoutNode.items.length;
8571
+ this.jsf.addItem({
8572
+ layoutNode: signal(layoutNode.items[index]),
8573
+ layoutIndex: signal(this.layoutIndex().concat(index)),
8574
+ dataIndex: signal(this.dataIndex().concat(index))
8575
+ });
8576
+ this.updateControl();
8577
+ }
8578
+ this.selectedItem = index;
8579
+ }
8580
+ updateControl() {
8581
+ const lastItem = this.layoutNode().items[this.layoutNode().items.length - 1];
8582
+ if (lastItem.type === '$ref' &&
8583
+ this.itemCount >= (lastItem.options.maxItems || 1000)) {
8584
+ this.showAddTab = false;
8585
+ }
8586
+ }
8587
+ setTabTitle(item, index) {
8588
+ return this.jsf.setArrayItemTitle(this, item, index);
8589
+ }
8590
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8591
+ 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: `
8592
+ <ul
8593
+ [class]="options?.labelHtmlClass || ''">
8594
+ <li *ngFor="let item of layoutNode()?.items; let i = index"
8595
+ [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8596
+ (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8597
+ (' ' + options?.style?.unselected))"
8598
+ role="presentation"
8599
+ data-tabs>
8600
+ <a *ngIf="showAddTab || item.type !== '$ref'"
8601
+ [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8602
+ (' ' + options?.style?.unselected))"
8603
+ (click)="select(i)">
8604
+ <input type="radio" [value]="i" *ngIf="options?.tabMode=='oneOfMode'"
8605
+ name="tabSelection"
8606
+ [(ngModel)]="selectedItem"
8607
+ [class]="(options?.widget_radioClass || '')"
8608
+ [value]="i"
8609
+ (change)="select(i)"
8610
+ />
8611
+ {{setTabTitle(item, i)}}
8612
+ </a>
8613
+ </li>
8614
+ </ul>
8615
+
8616
+ <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8617
+ [class]="(options?.htmlClass || '') + (selectedItem != i?' ngf-hidden':'') ">
8618
+ <!--for now the only difference between oneOfMode and the default
8619
+ is that oneOfMode uses the *ngIf="selectedItem === i" clause, which automatically
8620
+ destroys the tabs that are not rendered while default mode only hide them
8621
+ the upshot is that only the active tabs value will be used
8622
+ -->
8623
+ <ng-container *ngIf="options?.tabMode=='oneOfMode'">
8624
+ <select-framework-widget *ngIf="selectedItem === i"
8625
+ [class]="(options?.fieldHtmlClass || '') +
8626
+ ' ' + (options?.activeClass || '') +
8627
+ ' ' + (options?.style?.selected || '')"
8628
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8629
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8630
+ [layoutNode]="layoutItem"></select-framework-widget>
8631
+ </ng-container>
8632
+ <ng-container *ngIf="options?.tabMode !='oneOfMode'">
8633
+ <select-framework-widget
8634
+ [class]="(options?.fieldHtmlClass || '') +
8635
+ ' ' + (options?.activeClass || '') +
8636
+ ' ' + (options?.style?.selected || '')"
8637
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8638
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8639
+ [layoutNode]="layoutItem"></select-framework-widget>
8640
+ </ng-container>
8641
+ </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"] }] }); }
8642
+ }
8643
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabsComponent, decorators: [{
8644
+ type: Component,
8645
+ args: [{ selector: 'tabs-widget', template: `
8646
+ <ul
8647
+ [class]="options?.labelHtmlClass || ''">
8648
+ <li *ngFor="let item of layoutNode()?.items; let i = index"
8649
+ [class]="(options?.itemLabelHtmlClass || '') + (selectedItem === i ?
8650
+ (' ' + (options?.activeClass || '') + ' ' + (options?.style?.selected || '')) :
8651
+ (' ' + options?.style?.unselected))"
8652
+ role="presentation"
8653
+ data-tabs>
8654
+ <a *ngIf="showAddTab || item.type !== '$ref'"
8655
+ [class]="'nav-link' + (selectedItem === i ? (' ' + options?.activeClass + ' ' + options?.style?.selected) :
8656
+ (' ' + options?.style?.unselected))"
8657
+ (click)="select(i)">
8658
+ <input type="radio" [value]="i" *ngIf="options?.tabMode=='oneOfMode'"
8659
+ name="tabSelection"
8660
+ [(ngModel)]="selectedItem"
8661
+ [class]="(options?.widget_radioClass || '')"
8662
+ [value]="i"
8663
+ (change)="select(i)"
8664
+ />
8665
+ {{setTabTitle(item, i)}}
8666
+ </a>
8667
+ </li>
8668
+ </ul>
8669
+
8670
+ <div *ngFor="let layoutItem of layoutNode()?.items; let i = index"
8671
+ [class]="(options?.htmlClass || '') + (selectedItem != i?' ngf-hidden':'') ">
8672
+ <!--for now the only difference between oneOfMode and the default
8673
+ is that oneOfMode uses the *ngIf="selectedItem === i" clause, which automatically
8674
+ destroys the tabs that are not rendered while default mode only hide them
8675
+ the upshot is that only the active tabs value will be used
8676
+ -->
8677
+ <ng-container *ngIf="options?.tabMode=='oneOfMode'">
8678
+ <select-framework-widget *ngIf="selectedItem === i"
8679
+ [class]="(options?.fieldHtmlClass || '') +
8680
+ ' ' + (options?.activeClass || '') +
8681
+ ' ' + (options?.style?.selected || '')"
8682
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8683
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8684
+ [layoutNode]="layoutItem"></select-framework-widget>
8685
+ </ng-container>
8686
+ <ng-container *ngIf="options?.tabMode !='oneOfMode'">
8687
+ <select-framework-widget
8688
+ [class]="(options?.fieldHtmlClass || '') +
8689
+ ' ' + (options?.activeClass || '') +
8690
+ ' ' + (options?.style?.selected || '')"
8691
+ [dataIndex]="layoutNode()?.dataType === 'array' ? (dataIndex() || []).concat(i) : dataIndex()"
8692
+ [layoutIndex]="(layoutIndex() || []).concat(i)"
8693
+ [layoutNode]="layoutItem"></select-framework-widget>
8694
+ </ng-container>
8695
+ </div>`, standalone: false, styles: ["a{cursor:pointer}.ngf-hidden{display:none}\n"] }]
8696
+ }] });
8697
+
7919
8698
  // TODO: Add this control
7920
8699
  class OneOfComponent {
7921
8700
  constructor() {
@@ -7928,20 +8707,85 @@ class OneOfComponent {
7928
8707
  }
7929
8708
  ngOnInit() {
7930
8709
  this.options = this.layoutNode().options || {};
8710
+ this.options.tabMode = "oneOfMode";
8711
+ this.options.selectedTab = this.findSelectedTab();
8712
+ //this.options.description=this.options.description||"choose one of";
7931
8713
  this.jsf.initializeControl(this);
7932
8714
  }
8715
+ findSelectedTab() {
8716
+ //TODO test- this.jsf.formValues seems to be the initial data supplied to the form
8717
+ //while the jsf.formGroup value is derived from the actual controls
8718
+ //let formValue=this.jsf.getFormControlValue(this);
8719
+ let foundInd = -1;
8720
+ //seach for non null value
8721
+ if (this.layoutNode().items) {
8722
+ this.layoutNode().items.forEach((layoutItem, ind) => {
8723
+ let formValue = JsonPointer.get(this.jsf.formValues, layoutItem.dataPointer);
8724
+ if (layoutItem.oneOfPointer) {
8725
+ let controlKey = path2ControlKey(layoutItem.oneOfPointer);
8726
+ let fname = layoutItem.name;
8727
+ if (hasOwn(this.jsf.formGroup.controls, controlKey) &&
8728
+ (formValue || hasNonNullValue(this.jsf.formGroup.controls[controlKey].value))
8729
+ //hasOwn(formValue,fname) && hasOwn(this.jsf.formGroup.controls,controlKey)
8730
+ // && (formValue[fname] || this.jsf.formGroup.controls[controlKey].value)
8731
+ //&&isEqual(formValue[fname],this.jsf.formGroup.controls[controlKey].value)
8732
+ ) {
8733
+ foundInd = ind;
8734
+ }
8735
+ //foundInd=formValue[controlKey]!=null?ind:foundInd;
8736
+ //if no exact match found, then search in descendant values
8737
+ //to see which one of item matches
8738
+ if (foundInd == -1) {
8739
+ //find all descendant oneof paths
8740
+ let descendantOneOfControlNames = Object.keys(this.jsf.formGroup.controls).filter(controlName => {
8741
+ return controlName.startsWith(controlKey);
8742
+ });
8743
+ descendantOneOfControlNames.forEach(controlName => {
8744
+ let parts = controlName.split('$');
8745
+ let fieldName = parts[parts.length - 1];
8746
+ let controlValue = this.jsf.formGroup.controls[controlName].value;
8747
+ if (isObject$1(formValue) && hasOwn(formValue, fieldName) &&
8748
+ formValue[fieldName] == controlValue
8749
+ //formValue[fieldName]||controlValue
8750
+ ) {
8751
+ foundInd = ind;
8752
+ }
8753
+ else //if(formValue || controlValue){
8754
+ if (isEqual$2(formValue, controlValue)) {
8755
+ foundInd = ind;
8756
+ }
8757
+ });
8758
+ //now need to compare values
8759
+ }
8760
+ }
8761
+ });
8762
+ }
8763
+ return Math.max(foundInd, 0);
8764
+ }
7933
8765
  updateValue(event) {
7934
8766
  this.jsf.updateValue(this, event.target.value);
7935
8767
  }
8768
+ ngOnDestroy() {
8769
+ //this.jsf.updateValue(this, null);
8770
+ }
7936
8771
  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 }); }
8772
+ 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>
8773
+ <tabs-widget #tabs [layoutNode]="layoutNode()"
8774
+ [layoutIndex]="layoutIndex()"
8775
+ [dataIndex]="dataIndex()" >
8776
+ </tabs-widget>`, isInline: true, dependencies: [{ kind: "component", type: TabsComponent, selector: "tabs-widget", inputs: ["layoutNode", "layoutIndex", "dataIndex"] }] }); }
7938
8777
  }
7939
8778
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: OneOfComponent, decorators: [{
7940
8779
  type: Component,
7941
8780
  args: [{
7942
8781
  // tslint:disable-next-line:component-selector
7943
8782
  selector: 'one-of-widget',
7944
- template: ``,
8783
+ template: `<h4>{{this.options?.description}}</h4>
8784
+ <tabs-widget #tabs [layoutNode]="layoutNode()"
8785
+ [layoutIndex]="layoutIndex()"
8786
+ [dataIndex]="dataIndex()" >
8787
+ </tabs-widget>`,
8788
+ standalone: false
7945
8789
  }]
7946
8790
  }] });
7947
8791
 
@@ -7969,6 +8813,9 @@ class RadiosComponent {
7969
8813
  updateValue(event) {
7970
8814
  this.jsf.updateValue(this, event.target.value);
7971
8815
  }
8816
+ ngOnDestroy() {
8817
+ this.jsf.updateValue(this, null);
8818
+ }
7972
8819
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RadiosComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7973
8820
  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
8821
  <label *ngIf="options?.title"
@@ -8091,46 +8938,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8091
8938
  }]
8092
8939
  }] });
8093
8940
 
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
8941
  /**
8135
8942
  * OrderableDirective
8136
8943
  *
@@ -8286,6 +9093,12 @@ class RootComponent {
8286
9093
  };
8287
9094
  //must set moveLayout to false as nxtSortable already moves it
8288
9095
  this.jsf.moveArrayItem(itemCtx, evt.oldIndex, evt.newIndex, false);
9096
+ },
9097
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
9098
+ if (evt.related.classList.contains("sortable-fixed")) {
9099
+ //console.log(evt.related);
9100
+ return false;
9101
+ }
8289
9102
  }
8290
9103
  };
8291
9104
  }
@@ -8302,6 +9115,12 @@ class RootComponent {
8302
9115
  }
8303
9116
  return result;
8304
9117
  }
9118
+ //TODO also need to think of other types such as button which can be
9119
+ //created by an arbitrary layout
9120
+ isFixed(node) {
9121
+ let result = node.type == '$ref';
9122
+ return result;
9123
+ }
8305
9124
  // Set attributes for flexbox child
8306
9125
  // (container attributes are set in section.component)
8307
9126
  getFlexAttribute(node, attribute) {
@@ -8341,6 +9160,7 @@ class RootComponent {
8341
9160
  [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8342
9161
  [style.order]="(layoutItem.options || {}).order"
8343
9162
  [class.sortable-filter]="!isDraggable(layoutItem)"
9163
+ [class.sortable-fixed]="isFixed(layoutItem)"
8344
9164
  >
8345
9165
  <!--NB orderable directive is not used but has been left in for now and set to false
8346
9166
  otherwise the compiler won't recognize dataIndex and other dependent attributes
@@ -8351,6 +9171,7 @@ class RootComponent {
8351
9171
  [layoutNode]="layoutItem"
8352
9172
  [orderable]="false"
8353
9173
  [class.sortable-filter]="!isDraggable(layoutItem)"
9174
+ [class.sortable-fixed]="isFixed(layoutItem)"
8354
9175
  >
8355
9176
  <select-framework-widget *ngIf="showWidget(layoutItem)"
8356
9177
  [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
@@ -8373,6 +9194,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8373
9194
  [style.flex-shrink]="getFlexAttribute(layoutItem, 'flex-shrink')"
8374
9195
  [style.order]="(layoutItem.options || {}).order"
8375
9196
  [class.sortable-filter]="!isDraggable(layoutItem)"
9197
+ [class.sortable-fixed]="isFixed(layoutItem)"
8376
9198
  >
8377
9199
  <!--NB orderable directive is not used but has been left in for now and set to false
8378
9200
  otherwise the compiler won't recognize dataIndex and other dependent attributes
@@ -8383,6 +9205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8383
9205
  [layoutNode]="layoutItem"
8384
9206
  [orderable]="false"
8385
9207
  [class.sortable-filter]="!isDraggable(layoutItem)"
9208
+ [class.sortable-fixed]="isFixed(layoutItem)"
8386
9209
  >
8387
9210
  <select-framework-widget *ngIf="showWidget(layoutItem)"
8388
9211
  [dataIndex]="layoutItem?.arrayItem ? (dataIndex() || []).concat(i) : (dataIndex() || [])"
@@ -8591,6 +9414,7 @@ class SelectComponent {
8591
9414
  this.controlDisabled = false;
8592
9415
  this.boundControl = false;
8593
9416
  this.selectList = [];
9417
+ this.selectListFlatGroup = [];
8594
9418
  this.isArray = isArray;
8595
9419
  this.layoutNode = input(undefined);
8596
9420
  this.layoutIndex = input(undefined);
@@ -8599,10 +9423,40 @@ class SelectComponent {
8599
9423
  ngOnInit() {
8600
9424
  this.options = this.layoutNode().options || {};
8601
9425
  this.selectList = buildTitleMap(this.options.titleMap || this.options.enumNames, this.options.enum, !!this.options.required, !!this.options.flatList);
9426
+ //the selectListFlatGroup array will be used to update the formArray values
9427
+ //while the selectList array will be bound to the form select
9428
+ //as either a grouped select or a flat select
9429
+ this.selectListFlatGroup = buildTitleMap(this.options.titleMap || this.options.enumNames, this.options.enum, !!this.options.required, true);
8602
9430
  this.jsf.initializeControl(this);
8603
9431
  }
9432
+ deselectAll() {
9433
+ this.selectListFlatGroup.forEach(selItem => {
9434
+ selItem.checked = false;
9435
+ });
9436
+ }
8604
9437
  updateValue(event) {
8605
- this.jsf.updateValue(this, event.target.value);
9438
+ this.options.showErrors = true;
9439
+ if (this.options.multiple) {
9440
+ if (this.controlValue?.includes(null)) {
9441
+ this.deselectAll();
9442
+ //this.control.setValue([]); // Reset the form control to an empty array
9443
+ //this.selectList=JSON.parse(JSON.stringify(this.selectList));
9444
+ this.jsf.updateArrayMultiSelectList(this, []);
9445
+ }
9446
+ else {
9447
+ this.selectListFlatGroup.forEach(selItem => {
9448
+ selItem.checked = this.controlValue?.indexOf(selItem.value) >= 0 ? true : false;
9449
+ });
9450
+ this.jsf.updateArrayMultiSelectList(this, this.selectListFlatGroup);
9451
+ }
9452
+ return;
9453
+ }
9454
+ this.jsf.updateValue(this, this.controlValue);
9455
+ }
9456
+ ngOnDestroy() {
9457
+ let nullVal = this.options.multiple ? [null] : null;
9458
+ this.formControl.reset(nullVal);
9459
+ this.controlValue = null;
8606
9460
  }
8607
9461
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8608
9462
  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 +9467,7 @@ class SelectComponent {
8613
9467
  [class]="options?.labelHtmlClass || ''"
8614
9468
  [style.display]="options?.notitle ? 'none' : ''"
8615
9469
  [innerHTML]="options?.title"></label>
8616
- <select *ngIf="boundControl"
9470
+ <select *ngIf="boundControl && !options?.multiple"
8617
9471
  [formControl]="formControl"
8618
9472
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
8619
9473
  [attr.readonly]="options?.readonly ? 'readonly' : null"
@@ -8660,7 +9514,34 @@ class SelectComponent {
8660
9514
  </optgroup>
8661
9515
  </ng-template>
8662
9516
  </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"] }] }); }
9517
+ <select *ngIf="boundControl && options?.multiple"
9518
+ [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
9519
+ [attr.readonly]="options?.readonly ? 'readonly' : null"
9520
+ [attr.required]="options?.required"
9521
+ [class]="options?.fieldHtmlClass || ''"
9522
+ [disabled]="controlDisabled"
9523
+ [id]="'control' + layoutNode()?._id"
9524
+ [multiple]="options?.multiple"
9525
+ [name]="controlName"
9526
+ [(ngModel)]="controlValue"
9527
+ (change)="updateValue($event)">
9528
+ <ng-template ngFor let-selectItem [ngForOf]="selectList">
9529
+ <option *ngIf="!isArray(selectItem?.items)"
9530
+ [selected]="selectItem?.value === controlValue"
9531
+ [value]="selectItem?.value">
9532
+ <span [innerHTML]="selectItem?.name"></span>
9533
+ </option>
9534
+ <optgroup *ngIf="isArray(selectItem?.items)"
9535
+ [label]="selectItem?.group">
9536
+ <option *ngFor="let subItem of selectItem.items"
9537
+ [attr.selected]="subItem?.value === controlValue"
9538
+ [value]="subItem?.value">
9539
+ <span [innerHTML]="subItem?.name"></span>
9540
+ </option>
9541
+ </optgroup>
9542
+ </ng-template>
9543
+ </select>
9544
+ </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
9545
  }
8665
9546
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, decorators: [{
8666
9547
  type: Component,
@@ -8675,7 +9556,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8675
9556
  [class]="options?.labelHtmlClass || ''"
8676
9557
  [style.display]="options?.notitle ? 'none' : ''"
8677
9558
  [innerHTML]="options?.title"></label>
8678
- <select *ngIf="boundControl"
9559
+ <select *ngIf="boundControl && !options?.multiple"
8679
9560
  [formControl]="formControl"
8680
9561
  [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
8681
9562
  [attr.readonly]="options?.readonly ? 'readonly' : null"
@@ -8722,6 +9603,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8722
9603
  </optgroup>
8723
9604
  </ng-template>
8724
9605
  </select>
9606
+ <select *ngIf="boundControl && options?.multiple"
9607
+ [attr.aria-describedby]="'control' + layoutNode()?._id + 'Status'"
9608
+ [attr.readonly]="options?.readonly ? 'readonly' : null"
9609
+ [attr.required]="options?.required"
9610
+ [class]="options?.fieldHtmlClass || ''"
9611
+ [disabled]="controlDisabled"
9612
+ [id]="'control' + layoutNode()?._id"
9613
+ [multiple]="options?.multiple"
9614
+ [name]="controlName"
9615
+ [(ngModel)]="controlValue"
9616
+ (change)="updateValue($event)">
9617
+ <ng-template ngFor let-selectItem [ngForOf]="selectList">
9618
+ <option *ngIf="!isArray(selectItem?.items)"
9619
+ [selected]="selectItem?.value === controlValue"
9620
+ [value]="selectItem?.value">
9621
+ <span [innerHTML]="selectItem?.name"></span>
9622
+ </option>
9623
+ <optgroup *ngIf="isArray(selectItem?.items)"
9624
+ [label]="selectItem?.group">
9625
+ <option *ngFor="let subItem of selectItem.items"
9626
+ [attr.selected]="subItem?.value === controlValue"
9627
+ [value]="subItem?.value">
9628
+ <span [innerHTML]="subItem?.name"></span>
9629
+ </option>
9630
+ </optgroup>
9631
+ </ng-template>
9632
+ </select>
8725
9633
  </div>`,
8726
9634
  }]
8727
9635
  }] });
@@ -8738,6 +9646,7 @@ class SubmitComponent {
8738
9646
  ngOnDestroy() {
8739
9647
  this.isValidChangesSubs?.unsubscribe();
8740
9648
  this.isValidChangesSubs = null;
9649
+ this.updateValue({ target: { value: null } });
8741
9650
  }
8742
9651
  ngOnInit() {
8743
9652
  this.options = this.layoutNode().options || {};
@@ -8835,107 +9744,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8835
9744
  }]
8836
9745
  }] });
8837
9746
 
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
9747
  class TemplateComponent {
8940
9748
  constructor() {
8941
9749
  this.jsf = inject(JsonSchemaFormService);
@@ -8991,6 +9799,9 @@ class TextareaComponent {
8991
9799
  updateValue(event) {
8992
9800
  this.jsf.updateValue(this, event.target.value);
8993
9801
  }
9802
+ ngOnDestroy() {
9803
+ this.jsf.updateValue(this, null);
9804
+ }
8994
9805
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8995
9806
  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
9807
  <div
@@ -9291,7 +10102,13 @@ class WidgetLibraryModule {
9291
10102
  //disabled:false,
9292
10103
  //draggable:".draggableitem",//">:not(.nonsort)",//">.draggable-item",//":not(.nonsort)",//">*",//":not(.nonsort)",//":not(.non-draggable)",
9293
10104
  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
10105
+ preventOnFilter: false, //needed for input range elements slider do still work
10106
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
10107
+ if (evt.related.classList.contains("sortable-fixed")) {
10108
+ //console.log(evt.related);
10109
+ return false;
10110
+ }
10111
+ }
9295
10112
  })] }); }
9296
10113
  }
9297
10114
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WidgetLibraryModule, decorators: [{
@@ -9302,7 +10119,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
9302
10119
  //disabled:false,
9303
10120
  //draggable:".draggableitem",//">:not(.nonsort)",//">.draggable-item",//":not(.nonsort)",//">*",//":not(.nonsort)",//":not(.non-draggable)",
9304
10121
  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
10122
+ preventOnFilter: false, //needed for input range elements slider do still work
10123
+ onMove: function (/**Event*/ evt, /**Event*/ originalEvent) {
10124
+ if (evt.related.classList.contains("sortable-fixed")) {
10125
+ //console.log(evt.related);
10126
+ return false;
10127
+ }
10128
+ }
9306
10129
  })],
9307
10130
  declarations: [...BASIC_WIDGETS, OrderableDirective, ElementAttributeDirective],
9308
10131
  exports: [...BASIC_WIDGETS, OrderableDirective, ElementAttributeDirective]
@@ -9547,7 +10370,7 @@ class JsonSchemaFormComponent {
9547
10370
  this.previousInputs = {
9548
10371
  schema: null, layout: null, data: null, options: null, framework: null,
9549
10372
  widgets: null, form: null, model: null, JSONSchema: null, UISchema: null,
9550
- formData: null, loadExternalAssets: null, debug: null,
10373
+ formData: null, loadExternalAssets: null, debug: null, ajvOptions: null
9551
10374
  };
9552
10375
  // Recommended inputs
9553
10376
  this.schema = input(undefined); // The JSON Schema
@@ -9570,6 +10393,7 @@ class JsonSchemaFormComponent {
9570
10393
  this.loadExternalAssets = input(undefined); // Load external framework assets?
9571
10394
  this.debug = input(undefined); // Show debug information?
9572
10395
  this.theme = input(undefined); // Theme
10396
+ this.ajvOptions = input(undefined); // ajvOptions
9573
10397
  // Outputs
9574
10398
  this.onChanges = output(); // Live unvalidated internal form data
9575
10399
  this.onSubmit = output(); // Complete validated form data
@@ -9791,6 +10615,7 @@ class JsonSchemaFormComponent {
9791
10615
  this.jsf.data) {
9792
10616
  // Reset all form values to defaults
9793
10617
  this.jsf.resetAllValues();
10618
+ this.initializeAjv();
9794
10619
  this.initializeOptions(); // Update options
9795
10620
  this.initializeSchema(); // Update schema, schemaRefLibrary,
9796
10621
  // schemaRecursiveRefMap, & dataRecursiveRefMap
@@ -9841,6 +10666,18 @@ class JsonSchemaFormComponent {
9841
10666
  this.formInitialized = true;
9842
10667
  }
9843
10668
  }
10669
+ /**
10670
+ * 'initializeAjv' function
10671
+ *
10672
+ * Initialize ajv from 'ajvOptions'
10673
+ */
10674
+ initializeAjv() {
10675
+ const form = this.form();
10676
+ const ajvOptions = cloneDeep(this.ajvOptions()) || cloneDeep(form.ajvOptions);
10677
+ if (ajvOptions) {
10678
+ this.ajvInstanceName = this.jsf.createAndRegisterAjvInstance(ajvOptions).name;
10679
+ }
10680
+ }
9844
10681
  /**
9845
10682
  * 'initializeOptions' function
9846
10683
  *
@@ -9964,7 +10801,8 @@ class JsonSchemaFormComponent {
9964
10801
  // draft 3 (JSON Form style) and draft 4 (Angular Schema Form style)
9965
10802
  this.jsf.schema = convertSchemaToDraft6(this.jsf.schema);
9966
10803
  // Initialize ajv and compile schema
9967
- this.jsf.compileAjvSchema();
10804
+ //this.jsf.compileAjvSchema();
10805
+ //moved to initializeAjv()
9968
10806
  // Create schemaRefLibrary, schemaRecursiveRefMap, dataRecursiveRefMap, & arrayMap
9969
10807
  this.jsf.schema = resolveSchemaReferences(this.jsf.schema, this.jsf.schemaRefLibrary, this.jsf.schemaRecursiveRefMap, this.jsf.dataRecursiveRefMap, this.jsf.arrayMap);
9970
10808
  if (hasOwn(this.jsf.schemaRefLibrary, '')) {
@@ -10168,7 +11006,8 @@ class JsonSchemaFormComponent {
10168
11006
  }
10169
11007
  if (!isEmpty(this.jsf.schema)) {
10170
11008
  // If not already initialized, initialize ajv and compile schema
10171
- this.jsf.compileAjvSchema();
11009
+ //this.jsf.compileAjvSchema();
11010
+ //moved to initializeAjv()
10172
11011
  // Update all layout elements, add values, widgets, and validators,
10173
11012
  // replace any '*' with a layout built from all schema elements,
10174
11013
  // and update the FormGroup template with any new validators
@@ -10176,7 +11015,7 @@ class JsonSchemaFormComponent {
10176
11015
  // Build the Angular FormGroup template from the schema
10177
11016
  this.jsf.buildFormGroupTemplate(this.jsf.formValues);
10178
11017
  // Build the real Angular FormGroup from the FormGroup template
10179
- this.jsf.buildFormGroup();
11018
+ this.jsf.buildFormGroup(this.ajvInstanceName);
10180
11019
  }
10181
11020
  if (this.jsf.formGroup) {
10182
11021
  // Reset initial form values
@@ -10227,7 +11066,7 @@ class JsonSchemaFormComponent {
10227
11066
  }
10228
11067
  }
10229
11068
  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 }); }
11069
+ 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
11070
  }
10232
11071
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: JsonSchemaFormComponent, decorators: [{
10233
11072
  type: Component,
@@ -10263,5 +11102,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
10263
11102
  * Generated bundle index. Do not edit.
10264
11103
  */
10265
11104
 
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 };
11105
+ 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
11106
  //# sourceMappingURL=ng-formworks-core.mjs.map