jedison 1.5.1 → 1.6.1

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.
@@ -32,7 +32,7 @@ function replaceAll(str, find, replace) {
32
32
  return str.replace(new RegExp(escapeRegExp(find), "g"), replace);
33
33
  }
34
34
  function pathToAttribute(path) {
35
- return replaceAll(replaceAll(path, "#", "root"), "/", "-");
35
+ return replaceAll(replaceAll(path, "#", "root"), "/", "-").replace(/[^a-zA-Z0-9_-]/g, "");
36
36
  }
37
37
  function hasOwn(obj, prop) {
38
38
  return Object.prototype.hasOwnProperty.call(obj, prop);
@@ -220,6 +220,19 @@ function removeDuplicatesFromArray(arr) {
220
220
  }
221
221
  return uniqueObjects;
222
222
  }
223
+ function resolveInstancePath(currentPath, sourcePath) {
224
+ if (sourcePath.startsWith("#")) return sourcePath;
225
+ const parts = currentPath.split("/");
226
+ parts.pop();
227
+ for (const part of sourcePath.split("/")) {
228
+ if (part === "..") {
229
+ if (parts.length > 1) parts.pop();
230
+ } else if (part !== "." && part !== "") {
231
+ parts.push(part);
232
+ }
233
+ }
234
+ return parts.join("/");
235
+ }
223
236
  function generateRandomID(maxLength2) {
224
237
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
225
238
  let randomID = "";
@@ -510,14 +523,33 @@ function allOf(context) {
510
523
  let errors = [];
511
524
  const allOf2 = getSchemaAllOf(context.schema);
512
525
  if (isSet(allOf2)) {
513
- allOf2.forEach((schema) => {
514
- const subSchemaErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
515
- subSchemaErrors.forEach((error) => {
516
- error.path = context.path;
526
+ const enableSubErrors = getSchemaXOption(context.schema, "subErrors") ?? context.validator.subErrors;
527
+ if (enableSubErrors) {
528
+ const schemaResults = [];
529
+ allOf2.forEach((schema, index2) => {
530
+ const subSchemaErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
531
+ if (subSchemaErrors.length > 0) {
532
+ schemaResults.push({ schemaIndex: index2, errors: subSchemaErrors });
533
+ }
517
534
  });
518
- errors.push(...subSchemaErrors);
519
- });
520
- errors = removeDuplicatesFromArray(errors);
535
+ if (schemaResults.length > 0) {
536
+ errors.push({
537
+ type: "error",
538
+ path: context.path,
539
+ constraint: "allOf",
540
+ subErrors: schemaResults
541
+ });
542
+ }
543
+ } else {
544
+ allOf2.forEach((schema) => {
545
+ const subSchemaErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
546
+ subSchemaErrors.forEach((error) => {
547
+ error.path = context.path;
548
+ });
549
+ errors.push(...subSchemaErrors);
550
+ });
551
+ errors = removeDuplicatesFromArray(errors);
552
+ }
521
553
  }
522
554
  return errors;
523
555
  }
@@ -546,23 +578,46 @@ function anyOf(context) {
546
578
  const errors = [];
547
579
  const anyOf2 = getSchemaAnyOf(context.schema);
548
580
  if (isSet(anyOf2)) {
581
+ const enableSubErrors = getSchemaXOption(context.schema, "subErrors") ?? context.validator.subErrors;
549
582
  let valid = false;
550
- for (const schema of anyOf2) {
551
- const anyOfErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
552
- if (anyOfErrors.length === 0) {
553
- valid = true;
554
- break;
555
- }
556
- }
557
- if (!valid) {
558
- errors.push({
559
- type: "error",
560
- path: context.path,
561
- constraint: "anyOf",
562
- messages: [
563
- context.translator.translate("errorAnyOf")
564
- ]
583
+ if (enableSubErrors) {
584
+ const schemaResults = [];
585
+ anyOf2.forEach((schema, index2) => {
586
+ const anyOfErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
587
+ if (anyOfErrors.length === 0) {
588
+ valid = true;
589
+ }
590
+ schemaResults.push({ schemaIndex: index2, errors: anyOfErrors });
565
591
  });
592
+ if (!valid) {
593
+ errors.push({
594
+ type: "error",
595
+ path: context.path,
596
+ constraint: "anyOf",
597
+ messages: [
598
+ context.translator.translate("errorAnyOf")
599
+ ],
600
+ subErrors: schemaResults.filter((r) => r.errors.length > 0)
601
+ });
602
+ }
603
+ } else {
604
+ for (const schema of anyOf2) {
605
+ const anyOfErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
606
+ if (anyOfErrors.length === 0) {
607
+ valid = true;
608
+ break;
609
+ }
610
+ }
611
+ if (!valid) {
612
+ errors.push({
613
+ type: "error",
614
+ path: context.path,
615
+ constraint: "anyOf",
616
+ messages: [
617
+ context.translator.translate("errorAnyOf")
618
+ ]
619
+ });
620
+ }
566
621
  }
567
622
  }
568
623
  return errors;
@@ -630,10 +685,8 @@ function exclusiveMinimum(context) {
630
685
  function format(context) {
631
686
  const errors = [];
632
687
  const format2 = getSchemaFormat(context.schema);
633
- let assertFormat = context.validator.assertFormat;
634
- if (getSchemaXOption(context.schema, "assertFormat")) {
635
- assertFormat = context.schema.options.assertFormat;
636
- }
688
+ const xAssertFormat = getSchemaXOption(context.schema, "assertFormat");
689
+ const assertFormat = xAssertFormat !== void 0 ? xAssertFormat : context.validator.assertFormat;
637
690
  if (isSet(format2) && isString(context.value) && assertFormat) {
638
691
  let regexp;
639
692
  if (format2 === "email") {
@@ -673,18 +726,23 @@ function items(context) {
673
726
  messages: [context.translator.translate("errorItems")]
674
727
  });
675
728
  } else if (isObject(items2)) {
729
+ const enableSubErrors = getSchemaXOption(context.schema, "subErrors") ?? context.validator.subErrors;
676
730
  context.value.slice(prefixItemsSchemasCount).forEach((itemValue, i) => {
677
731
  const index2 = prefixItemsSchemasCount + i;
678
732
  const tmpErrors = context.validator.getErrors(itemValue, items2, index2, context.path + "/" + index2);
679
733
  if (tmpErrors.length > 0) {
680
- errors.push({
734
+ const error = {
681
735
  type: "error",
682
736
  path: context.path,
683
737
  constraint: "items",
684
738
  messages: [
685
739
  compileTemplate(context.translator.translate("errorItems"), { index: index2 })
686
740
  ]
687
- });
741
+ };
742
+ if (enableSubErrors) {
743
+ error.subErrors = tmpErrors;
744
+ }
745
+ errors.push(error);
688
746
  }
689
747
  });
690
748
  }
@@ -862,23 +920,64 @@ function oneOf(context) {
862
920
  const oneOf2 = getSchemaOneOf(context.schema);
863
921
  if (isSet(oneOf2)) {
864
922
  let counter = 0;
865
- oneOf2.forEach((schema) => {
866
- const oneOfErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
867
- if (oneOfErrors.length === 0) {
868
- counter++;
923
+ const enableSubErrors = getSchemaXOption(context.schema, "subErrors") ?? context.validator.subErrors;
924
+ if (enableSubErrors) {
925
+ const schemaResults = [];
926
+ oneOf2.forEach((schema, index2) => {
927
+ const oneOfErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
928
+ if (oneOfErrors.length === 0) {
929
+ counter++;
930
+ schemaResults.push({ schemaIndex: index2, matched: true, errors: [] });
931
+ } else {
932
+ schemaResults.push({ schemaIndex: index2, matched: false, errors: oneOfErrors });
933
+ }
934
+ });
935
+ if (counter !== 1) {
936
+ const error = {
937
+ type: "error",
938
+ path: context.path,
939
+ constraint: "oneOf",
940
+ messages: [
941
+ compileTemplate(context.translator.translate("errorOneOf"), {
942
+ counter
943
+ })
944
+ ]
945
+ };
946
+ if (counter === 0) {
947
+ error.subErrors = schemaResults.filter((r) => !r.matched).map((r) => ({
948
+ schemaIndex: r.schemaIndex,
949
+ errors: r.errors
950
+ }));
951
+ } else {
952
+ error.matchingSchemas = schemaResults.filter((r) => r.matched).map((r) => r.schemaIndex);
953
+ }
954
+ errors.push(error);
869
955
  }
870
- });
871
- if (counter !== 1) {
872
- errors.push({
873
- type: "error",
874
- path: context.path,
875
- constraint: "oneOf",
876
- messages: [
877
- compileTemplate(context.translator.translate("errorOneOf"), {
878
- counter
879
- })
880
- ]
956
+ } else {
957
+ const matchingSchemas = [];
958
+ oneOf2.forEach((schema, index2) => {
959
+ const oneOfErrors = context.validator.getErrors(context.value, schema, context.key, context.path);
960
+ if (oneOfErrors.length === 0) {
961
+ counter++;
962
+ matchingSchemas.push(index2);
963
+ }
881
964
  });
965
+ if (counter !== 1) {
966
+ const error = {
967
+ type: "error",
968
+ path: context.path,
969
+ constraint: "oneOf",
970
+ messages: [
971
+ compileTemplate(context.translator.translate("errorOneOf"), {
972
+ counter
973
+ })
974
+ ]
975
+ };
976
+ if (counter > 0) {
977
+ error.matchingSchemas = matchingSchemas;
978
+ }
979
+ errors.push(error);
980
+ }
882
981
  }
883
982
  }
884
983
  return errors;
@@ -931,6 +1030,8 @@ function patternProperties(context) {
931
1030
  function properties(context) {
932
1031
  const schemaProperties = getSchemaProperties(context.schema);
933
1032
  const invalidProperties = [];
1033
+ const enableSubErrors = getSchemaXOption(context.schema, "subErrors") ?? context.validator.subErrors;
1034
+ const propertySubErrors = [];
934
1035
  if (isObject(context.value) && isSet(schemaProperties)) {
935
1036
  Object.keys(schemaProperties).forEach((propertyName) => {
936
1037
  if (hasOwn(context.value, propertyName)) {
@@ -943,19 +1044,26 @@ function properties(context) {
943
1044
  );
944
1045
  if (propertyErrors.length > 0) {
945
1046
  invalidProperties.push(propertyName);
1047
+ if (enableSubErrors) {
1048
+ propertySubErrors.push({ property: propertyName, errors: propertyErrors });
1049
+ }
946
1050
  }
947
1051
  }
948
1052
  });
949
1053
  }
950
1054
  if (invalidProperties.length > 0) {
951
- return [{
1055
+ const error = {
952
1056
  type: "error",
953
1057
  path: context.path,
954
1058
  constraint: "properties",
955
1059
  messages: [
956
1060
  compileTemplate(context.translator.translate("errorProperties"), { properties: invalidProperties.join(", ") })
957
1061
  ]
958
- }];
1062
+ };
1063
+ if (enableSubErrors) {
1064
+ error.subErrors = propertySubErrors;
1065
+ }
1066
+ return [error];
959
1067
  }
960
1068
  return [];
961
1069
  }
@@ -1232,9 +1340,11 @@ function dependentRequired(context) {
1232
1340
  Object.keys(dependentRequired2).forEach((key) => {
1233
1341
  if (isSet(context.value[key])) {
1234
1342
  const requiredProperties = dependentRequired2[key];
1235
- missingProperties = requiredProperties.filter((property) => {
1236
- return !hasOwn(context.value, property);
1237
- });
1343
+ if (isArray(requiredProperties)) {
1344
+ missingProperties = requiredProperties.filter((property) => {
1345
+ return !hasOwn(context.value, property);
1346
+ });
1347
+ }
1238
1348
  }
1239
1349
  });
1240
1350
  const invalid = missingProperties.length > 0;
@@ -1293,12 +1403,13 @@ function prefixItems(context) {
1293
1403
  const errors = [];
1294
1404
  const prefixItems2 = getSchemaPrefixItems(context.schema);
1295
1405
  if (isArray(context.value) && isSet(prefixItems2)) {
1406
+ const enableSubErrors = getSchemaXOption(context.schema, "subErrors") ?? context.validator.subErrors;
1296
1407
  prefixItems2.forEach((itemSchema, index2) => {
1297
1408
  const itemValue = context.value[index2];
1298
1409
  if (isSet(itemValue)) {
1299
1410
  const tmpErrors = context.validator.getErrors(itemValue, itemSchema, index2, context.path + "/" + index2);
1300
1411
  if (tmpErrors.length > 0) {
1301
- errors.push({
1412
+ const error = {
1302
1413
  type: "error",
1303
1414
  path: context.path,
1304
1415
  constraint: "prefixItems",
@@ -1307,7 +1418,11 @@ function prefixItems(context) {
1307
1418
  index: index2
1308
1419
  })
1309
1420
  ]
1310
- });
1421
+ };
1422
+ if (enableSubErrors) {
1423
+ error.subErrors = tmpErrors;
1424
+ }
1425
+ errors.push(error);
1311
1426
  }
1312
1427
  }
1313
1428
  });
@@ -1538,6 +1653,7 @@ class Validator {
1538
1653
  this.constraints = config.constraints ?? {};
1539
1654
  this.assertFormat = config.assertFormat ? config.assertFormat : false;
1540
1655
  this.translator = config.translator ? config.translator : false;
1656
+ this.subErrors = config.subErrors ?? false;
1541
1657
  this.draft = draft202012;
1542
1658
  this.jsonSchemaDrafts = {
1543
1659
  "http://json-schema.org/draft-04/schema#": draft04,
@@ -2123,11 +2239,13 @@ class Editor {
2123
2239
  });
2124
2240
  this.control.messages.innerHTML = "";
2125
2241
  this.showingValidationErrors = false;
2242
+ this.setAriaInvalid(false);
2126
2243
  const neverShowErrors = this.instance.jedison.options.showErrors === "never" || getSchemaXOption(this.instance.schema, "showErrors") === "never";
2127
2244
  if (neverShowErrors && !force || errors.length === 0) {
2128
2245
  return;
2129
2246
  }
2130
2247
  const muteValidationMessages = getSchemaXOption(this.instance.schema, "muteValidationMessages") ?? this.instance.jedison.options.muteValidationMessages ?? [];
2248
+ let hasErrors = false;
2131
2249
  errors.forEach((error) => {
2132
2250
  if (muteValidationMessages.includes(error.constraint)) {
2133
2251
  return;
@@ -2135,6 +2253,7 @@ class Editor {
2135
2253
  error.messages.forEach((message) => {
2136
2254
  let invalidFeedback;
2137
2255
  if (error.type === "error") {
2256
+ hasErrors = true;
2138
2257
  invalidFeedback = this.getErrorFeedback({
2139
2258
  message
2140
2259
  });
@@ -2146,8 +2265,20 @@ class Editor {
2146
2265
  this.control.messages.appendChild(invalidFeedback);
2147
2266
  });
2148
2267
  });
2268
+ if (hasErrors) {
2269
+ this.setAriaInvalid(true);
2270
+ }
2149
2271
  this.showingValidationErrors = true;
2150
2272
  }
2273
+ setAriaInvalid(invalid) {
2274
+ if (this.control.input) {
2275
+ if (invalid) {
2276
+ this.control.input.setAttribute("aria-invalid", "true");
2277
+ } else {
2278
+ this.control.input.removeAttribute("aria-invalid");
2279
+ }
2280
+ }
2281
+ }
2151
2282
  /**
2152
2283
  * Get an error message container
2153
2284
  */
@@ -2234,6 +2365,8 @@ class Editor {
2234
2365
  }
2235
2366
  return schemaInfo;
2236
2367
  }
2368
+ refreshLegendWarning() {
2369
+ }
2237
2370
  /**
2238
2371
  * Updates control UI when its state changes
2239
2372
  */
@@ -2294,6 +2427,24 @@ class Editor {
2294
2427
  this.control.jsonData.input.value = JSON.stringify(this.instance.getValue(), null, 2);
2295
2428
  }
2296
2429
  }
2430
+ getNextChildPath(path) {
2431
+ const currentDepth = this.instance.path.split(this.instance.jedison.pathSeparator).length;
2432
+ const targetSegments = path.split(this.instance.jedison.pathSeparator);
2433
+ if (targetSegments.length <= currentDepth) return null;
2434
+ return targetSegments.slice(0, currentDepth + 1).join(this.instance.jedison.pathSeparator);
2435
+ }
2436
+ navigateTo(path) {
2437
+ if (path === this.instance.path) {
2438
+ this.control.container.scrollIntoView({ behavior: "smooth", block: "nearest" });
2439
+ return;
2440
+ }
2441
+ const nextChildPath = this.getNextChildPath(path);
2442
+ if (!nextChildPath) return;
2443
+ const child = this.instance.children.find((c) => c.path === nextChildPath);
2444
+ if (child == null ? void 0 : child.ui) {
2445
+ child.ui.navigateTo(path);
2446
+ }
2447
+ }
2297
2448
  /**
2298
2449
  * Destroys the editor
2299
2450
  */
@@ -2635,6 +2786,21 @@ class InstanceMultiple extends Instance {
2635
2786
  * Returns the index of the instance that has less validation errors
2636
2787
  */
2637
2788
  getFittestIndex(value) {
2789
+ const discriminator = getSchemaXOption(this.schema, "discriminator");
2790
+ if (isSet(discriminator) && isObject(value)) {
2791
+ const propName = isString(discriminator) ? discriminator : discriminator.propertyName;
2792
+ const discriminatorValue = value[propName];
2793
+ if (isSet(discriminatorValue)) {
2794
+ for (let index2 = 0; index2 < this.schemas.length; index2++) {
2795
+ const schema = this.schemas[index2];
2796
+ const propSchema = schema.properties && schema.properties[propName];
2797
+ if (propSchema) {
2798
+ const propErrors = this.jedison.validator.getErrors(discriminatorValue, propSchema, propName, this.path);
2799
+ if (propErrors.length === 0) return index2;
2800
+ }
2801
+ }
2802
+ }
2803
+ }
2638
2804
  let fittestIndex;
2639
2805
  let championErrors;
2640
2806
  for (let index2 = 0; index2 < this.instances.length; index2++) {
@@ -2761,9 +2927,11 @@ class InstanceObject extends Instance {
2761
2927
  Object.keys(dependentRequired2).forEach((key) => {
2762
2928
  if (isSet(this.value[key])) {
2763
2929
  const requiredProperties = dependentRequired2[key];
2764
- missingProperties = requiredProperties.filter((property2) => {
2765
- return !hasOwn(this.value, property2);
2766
- });
2930
+ if (isArray(requiredProperties)) {
2931
+ missingProperties = requiredProperties.filter((property2) => {
2932
+ return !hasOwn(this.value, property2);
2933
+ });
2934
+ }
2767
2935
  }
2768
2936
  });
2769
2937
  return missingProperties.includes(property);
@@ -3149,6 +3317,15 @@ class EditorRadios extends EditorBoolean {
3149
3317
  radio.checked = radioValue === this.instance.getValue();
3150
3318
  });
3151
3319
  }
3320
+ setAriaInvalid(invalid) {
3321
+ this.control.radios.forEach((radio) => {
3322
+ if (invalid) {
3323
+ radio.setAttribute("aria-invalid", "true");
3324
+ } else {
3325
+ radio.removeAttribute("aria-invalid");
3326
+ }
3327
+ });
3328
+ }
3152
3329
  }
3153
3330
  class EditorBooleanSelect extends EditorBoolean {
3154
3331
  static resolves(schema) {
@@ -3218,18 +3395,86 @@ class EditorStringRadios extends EditorString {
3218
3395
  static resolves(schema) {
3219
3396
  return getSchemaType(schema) === "string" && (getSchemaXOption(schema, "format") === "radios" || getSchemaXOption(schema, "format") === "radios-inline");
3220
3397
  }
3398
+ init() {
3399
+ super.init();
3400
+ this.setupEnumSource();
3401
+ }
3402
+ setupEnumSource() {
3403
+ const enumSourceRaw = getSchemaXOption(this.instance.schema, "enumSource");
3404
+ if (!isSet(enumSourceRaw)) return;
3405
+ const enumSource = resolveInstancePath(this.instance.path, enumSourceRaw);
3406
+ const src = this.instance.jedison.getInstance(enumSource);
3407
+ if (src) this.enumSourceValues = src.getValue();
3408
+ this.instance.jedison.watch(enumSource, () => {
3409
+ if (!this.control) return;
3410
+ const s = this.instance.jedison.getInstance(enumSource);
3411
+ if (s) {
3412
+ this.enumSourceValues = s.getValue();
3413
+ this.refreshOptions();
3414
+ }
3415
+ });
3416
+ }
3417
+ getEnumSourceValues() {
3418
+ if (this.enumSourceValues !== void 0) {
3419
+ if (isArray(this.enumSourceValues)) return this.enumSourceValues;
3420
+ if (isObject(this.enumSourceValues)) return Object.keys(this.enumSourceValues);
3421
+ return [];
3422
+ }
3423
+ return getSchemaEnum(this.instance.schema) || [];
3424
+ }
3221
3425
  build() {
3426
+ const values = this.getEnumSourceValues();
3222
3427
  this.control = this.theme.getRadiosControl({
3223
3428
  title: this.getTitle(),
3224
3429
  description: this.getDescription(),
3225
- values: getSchemaEnum(this.instance.schema),
3226
- titles: getSchemaXOption(this.instance.schema, "enumTitles") || getSchemaEnum(this.instance.schema),
3430
+ values,
3431
+ titles: getSchemaXOption(this.instance.schema, "enumTitles") || values,
3227
3432
  id: this.getIdFromPath(this.instance.path),
3228
3433
  titleHidden: getSchemaXOption(this.instance.schema, "titleHidden"),
3229
3434
  inline: getSchemaXOption(this.instance.schema, "format") === "radios-inline",
3230
3435
  info: this.getInfo()
3231
3436
  });
3232
3437
  }
3438
+ refreshOptions() {
3439
+ const values = this.getEnumSourceValues();
3440
+ const titles = getSchemaXOption(this.instance.schema, "enumTitles") || values;
3441
+ const id = this.getIdFromPath(this.instance.path);
3442
+ const messagesId = id + "-messages";
3443
+ const descriptionId = id + "-description";
3444
+ const describedBy = messagesId + " " + descriptionId;
3445
+ this.control.radioControls.forEach((rc) => {
3446
+ if (rc.parentNode) rc.parentNode.removeChild(rc);
3447
+ });
3448
+ this.control.radios = [];
3449
+ this.control.labels = [];
3450
+ this.control.radioControls = [];
3451
+ this.control.labelTexts = [];
3452
+ values.forEach((value, index2) => {
3453
+ const radioControl = document.createElement("div");
3454
+ const radio = document.createElement("input");
3455
+ const label = document.createElement("label");
3456
+ const labelText = document.createElement("span");
3457
+ radio.setAttribute("type", "radio");
3458
+ radio.setAttribute("id", id + "-" + index2);
3459
+ radio.setAttribute("name", id);
3460
+ radio.setAttribute("value", value);
3461
+ radio.setAttribute("aria-describedby", describedBy);
3462
+ label.setAttribute("for", id + "-" + index2);
3463
+ label.classList.add("jedi-title");
3464
+ label.classList.add("jedi-label");
3465
+ labelText.textContent = titles && titles[index2] !== void 0 ? titles[index2] : value;
3466
+ radioControl.appendChild(radio);
3467
+ radioControl.appendChild(label);
3468
+ label.appendChild(labelText);
3469
+ this.control.radios.push(radio);
3470
+ this.control.labels.push(label);
3471
+ this.control.labelTexts.push(labelText);
3472
+ this.control.radioControls.push(radioControl);
3473
+ this.control.fieldset.insertBefore(radioControl, this.control.description);
3474
+ });
3475
+ this.addEventListeners();
3476
+ this.refreshUI();
3477
+ }
3233
3478
  adaptForTable() {
3234
3479
  this.theme.adaptForTableRadiosControl(this.control);
3235
3480
  }
@@ -3246,23 +3491,73 @@ class EditorStringRadios extends EditorString {
3246
3491
  radio.checked = radio.value === this.instance.getValue();
3247
3492
  });
3248
3493
  }
3494
+ setAriaInvalid(invalid) {
3495
+ this.control.radios.forEach((radio) => {
3496
+ if (invalid) {
3497
+ radio.setAttribute("aria-invalid", "true");
3498
+ } else {
3499
+ radio.removeAttribute("aria-invalid");
3500
+ }
3501
+ });
3502
+ }
3249
3503
  }
3250
3504
  class EditorStringSelect extends EditorString {
3251
3505
  static resolves(schema) {
3252
- return getSchemaType(schema) === "string" && isSet(getSchemaEnum(schema));
3506
+ return getSchemaType(schema) === "string" && (isSet(getSchemaEnum(schema)) || isSet(getSchemaXOption(schema, "enumSource")));
3507
+ }
3508
+ init() {
3509
+ super.init();
3510
+ this.setupEnumSource();
3511
+ }
3512
+ setupEnumSource() {
3513
+ const enumSourceRaw = getSchemaXOption(this.instance.schema, "enumSource");
3514
+ if (!isSet(enumSourceRaw)) return;
3515
+ const enumSource = resolveInstancePath(this.instance.path, enumSourceRaw);
3516
+ const src = this.instance.jedison.getInstance(enumSource);
3517
+ if (src) this.enumSourceValues = src.getValue();
3518
+ this.instance.jedison.watch(enumSource, () => {
3519
+ if (!this.control) return;
3520
+ const s = this.instance.jedison.getInstance(enumSource);
3521
+ if (s) {
3522
+ this.enumSourceValues = s.getValue();
3523
+ this.refreshOptions();
3524
+ }
3525
+ });
3526
+ }
3527
+ getEnumSourceValues() {
3528
+ if (this.enumSourceValues !== void 0) {
3529
+ if (isArray(this.enumSourceValues)) return this.enumSourceValues;
3530
+ if (isObject(this.enumSourceValues)) return Object.keys(this.enumSourceValues);
3531
+ return [];
3532
+ }
3533
+ return getSchemaEnum(this.instance.schema) || [];
3253
3534
  }
3254
3535
  build() {
3536
+ const values = this.getEnumSourceValues();
3255
3537
  this.control = this.theme.getSelectControl({
3256
3538
  title: this.getTitle(),
3257
3539
  description: this.getDescription(),
3258
- values: getSchemaEnum(this.instance.schema),
3259
- titles: getSchemaXOption(this.instance.schema, "enumTitles") || getSchemaEnum(this.instance.schema),
3540
+ values,
3541
+ titles: getSchemaXOption(this.instance.schema, "enumTitles") || values,
3260
3542
  id: this.getIdFromPath(this.instance.path),
3261
3543
  titleIconClass: getSchemaXOption(this.instance.schema, "titleIconClass"),
3262
3544
  titleHidden: getSchemaXOption(this.instance.schema, "titleHidden"),
3263
3545
  info: this.getInfo()
3264
3546
  });
3265
3547
  }
3548
+ refreshOptions() {
3549
+ const values = this.getEnumSourceValues();
3550
+ const titles = getSchemaXOption(this.instance.schema, "enumTitles") || values;
3551
+ const select = this.control.input;
3552
+ select.innerHTML = "";
3553
+ values.forEach((value, i) => {
3554
+ const option = document.createElement("option");
3555
+ option.setAttribute("value", value);
3556
+ option.textContent = titles && titles[i] !== void 0 ? titles[i] : value;
3557
+ select.appendChild(option);
3558
+ });
3559
+ this.refreshUI();
3560
+ }
3266
3561
  adaptForTable() {
3267
3562
  this.theme.adaptForTableSelectControl(this.control);
3268
3563
  }
@@ -3504,25 +3799,75 @@ class EditorNumberRadios extends EditorNumber {
3504
3799
  radio.checked = Number(radio.value) === Number(this.instance.getValue());
3505
3800
  });
3506
3801
  }
3802
+ setAriaInvalid(invalid) {
3803
+ this.control.radios.forEach((radio) => {
3804
+ if (invalid) {
3805
+ radio.setAttribute("aria-invalid", "true");
3806
+ } else {
3807
+ radio.removeAttribute("aria-invalid");
3808
+ }
3809
+ });
3810
+ }
3507
3811
  }
3508
3812
  class EditorNumberSelect extends EditorNumber {
3509
3813
  static resolves(schema) {
3510
3814
  const schemaType = getSchemaType(schema);
3511
3815
  const typeIsNumeric = schemaType === "number" || schemaType === "integer";
3512
- return typeIsNumeric && isSet(getSchemaEnum(schema));
3816
+ return typeIsNumeric && (isSet(getSchemaEnum(schema)) || isSet(getSchemaXOption(schema, "enumSource")));
3817
+ }
3818
+ init() {
3819
+ super.init();
3820
+ this.setupEnumSource();
3821
+ }
3822
+ setupEnumSource() {
3823
+ const enumSourceRaw = getSchemaXOption(this.instance.schema, "enumSource");
3824
+ if (!isSet(enumSourceRaw)) return;
3825
+ const enumSource = resolveInstancePath(this.instance.path, enumSourceRaw);
3826
+ const src = this.instance.jedison.getInstance(enumSource);
3827
+ if (src) this.enumSourceValues = src.getValue();
3828
+ this.instance.jedison.watch(enumSource, () => {
3829
+ if (!this.control) return;
3830
+ const s = this.instance.jedison.getInstance(enumSource);
3831
+ if (s) {
3832
+ this.enumSourceValues = s.getValue();
3833
+ this.refreshOptions();
3834
+ }
3835
+ });
3836
+ }
3837
+ getEnumSourceValues() {
3838
+ if (this.enumSourceValues !== void 0) {
3839
+ if (isArray(this.enumSourceValues)) return this.enumSourceValues;
3840
+ if (isObject(this.enumSourceValues)) return Object.keys(this.enumSourceValues);
3841
+ return [];
3842
+ }
3843
+ return getSchemaEnum(this.instance.schema) || [];
3513
3844
  }
3514
3845
  build() {
3846
+ const values = this.getEnumSourceValues();
3515
3847
  this.control = this.theme.getSelectControl({
3516
3848
  title: this.getTitle(),
3517
3849
  description: this.getDescription(),
3518
- values: getSchemaEnum(this.instance.schema),
3519
- titles: getSchemaXOption(this.instance.schema, "enumTitles") || getSchemaEnum(this.instance.schema),
3850
+ values,
3851
+ titles: getSchemaXOption(this.instance.schema, "enumTitles") || values,
3520
3852
  id: this.getIdFromPath(this.instance.path),
3521
3853
  titleIconClass: getSchemaXOption(this.instance.schema, "titleIconClass"),
3522
3854
  titleHidden: getSchemaXOption(this.instance.schema, "titleHidden"),
3523
3855
  info: this.getInfo()
3524
3856
  });
3525
3857
  }
3858
+ refreshOptions() {
3859
+ const values = this.getEnumSourceValues();
3860
+ const titles = getSchemaXOption(this.instance.schema, "enumTitles") || values;
3861
+ const select = this.control.input;
3862
+ select.innerHTML = "";
3863
+ values.forEach((value, i) => {
3864
+ const option = document.createElement("option");
3865
+ option.setAttribute("value", value);
3866
+ option.textContent = titles && titles[i] !== void 0 ? titles[i] : value;
3867
+ select.appendChild(option);
3868
+ });
3869
+ this.refreshUI();
3870
+ }
3526
3871
  adaptForTable() {
3527
3872
  this.theme.adaptForTableSelectControl(this.control);
3528
3873
  }
@@ -3645,6 +3990,10 @@ class EditorObject extends Editor {
3645
3990
  if (isSet(additionalProperties2) && additionalProperties2 === false) {
3646
3991
  addProperty = false;
3647
3992
  }
3993
+ const objectAdd = getSchemaXOption(this.instance.schema, "objectAdd") ?? this.instance.jedison.options.objectAdd;
3994
+ if (isSet(objectAdd) && objectAdd === false) {
3995
+ addProperty = false;
3996
+ }
3648
3997
  let enablePropertiesToggle = false;
3649
3998
  if (isSet(this.instance.jedison.options.enablePropertiesToggle)) {
3650
3999
  enablePropertiesToggle = this.instance.jedison.options.enablePropertiesToggle;
@@ -3671,30 +4020,30 @@ class EditorObject extends Editor {
3671
4020
  });
3672
4021
  this.control.jsonData.input.value = JSON.stringify(this.instance.getValue(), null, 2);
3673
4022
  }
4023
+ announcePropertyAdded(propertyName, child) {
4024
+ const schemaTitle = getSchemaTitle(child.schema);
4025
+ const label = isSet(schemaTitle) ? schemaTitle : propertyName;
4026
+ const ariaLiveMessage = this.theme.getAriaLiveMessage();
4027
+ ariaLiveMessage.textContent = label + " " + this.instance.jedison.translator.translate("objectPropertyAdded");
4028
+ this.control.ariaLive.appendChild(ariaLiveMessage);
4029
+ }
4030
+ addProperty(input, postAction) {
4031
+ const propertyName = input.value.split(" ").join("");
4032
+ if (propertyName.length === 0) return;
4033
+ if (isSet(this.instance.value[propertyName])) return;
4034
+ const schema = this.instance.getPropertySchema(propertyName);
4035
+ const child = this.instance.createChild(schema, propertyName);
4036
+ child.activate();
4037
+ this.instance.setValue(this.instance.value, true, "user");
4038
+ input.value = "";
4039
+ this.announcePropertyAdded(propertyName, child);
4040
+ postAction();
4041
+ }
3674
4042
  addEventListeners() {
3675
- this.control.addPropertyBtn.addEventListener("click", () => {
3676
- const propertyName = this.control.addPropertyControl.input.value.split(" ").join("");
3677
- const propertyNameEmpty = propertyName.length === 0;
3678
- if (propertyNameEmpty) {
3679
- return;
3680
- }
3681
- const propertyExist = isSet(this.instance.value[propertyName]);
3682
- if (propertyExist) {
3683
- return;
3684
- }
3685
- const schema = this.instance.getPropertySchema(propertyName);
3686
- const child = this.instance.createChild(schema, propertyName);
3687
- child.activate();
3688
- this.instance.setValue(this.instance.value, true, "user");
3689
- this.control.addPropertyControl.input.value = "";
3690
- const ariaLive = this.control.ariaLive;
3691
- const schemaTitle = getSchemaTitle(child.schema);
3692
- const label = isSet(schemaTitle) ? schemaTitle : propertyName;
3693
- const ariaLiveMessage = this.theme.getAriaLiveMessage();
3694
- ariaLiveMessage.textContent = label + " " + this.instance.jedison.translator.translate("objectPropertyAdded");
3695
- ariaLive.appendChild(ariaLiveMessage);
3696
- this.control.propertiesContainer.close();
3697
- this.control.propertiesContainer.showModal();
4043
+ this.control.quickAddPropertyBtn.addEventListener("click", () => {
4044
+ this.addProperty(this.control.quickAddPropertyControl.input, () => {
4045
+ this.control.quickAddPropertyContainer.close();
4046
+ });
3698
4047
  });
3699
4048
  this.control.jsonData.saveBtn.addEventListener("click", () => {
3700
4049
  try {
@@ -3724,9 +4073,7 @@ class EditorObject extends Editor {
3724
4073
  const declaredProperties = Object.keys(this.instance.properties);
3725
4074
  const instanceProperties = this.instance.children.map((child) => child.getKey());
3726
4075
  const properties2 = [.../* @__PURE__ */ new Set([...declaredProperties, ...instanceProperties])];
3727
- while (this.control.propertiesActivators.firstChild) {
3728
- this.control.propertiesActivators.removeChild(this.control.propertiesActivators.firstChild);
3729
- }
4076
+ this.control.propertiesActivators.replaceChildren();
3730
4077
  const {
3731
4078
  container: defaultGroupContainer,
3732
4079
  group: defaultGroup
@@ -3783,12 +4130,28 @@ class EditorObject extends Editor {
3783
4130
  checkbox.disabled = this.disabled || isRequired;
3784
4131
  checkbox.checked = hasOwn(currentValue, property);
3785
4132
  });
4133
+ const propGroupOrder = getSchemaXOption(this.instance.schema, "propGroupOrder");
4134
+ if (isSet(propGroupOrder) && Array.isArray(propGroupOrder)) {
4135
+ const orderedContainers = [defaultGroupContainer];
4136
+ propGroupOrder.forEach((groupName) => {
4137
+ if (isSet(propertiesGroups[groupName])) {
4138
+ orderedContainers.push(propertiesGroups[groupName].container);
4139
+ }
4140
+ });
4141
+ Object.keys(propertiesGroups).forEach((groupName) => {
4142
+ if (!propGroupOrder.includes(groupName)) {
4143
+ orderedContainers.push(propertiesGroups[groupName].container);
4144
+ }
4145
+ });
4146
+ this.control.propertiesActivators.replaceChildren();
4147
+ orderedContainers.forEach((container) => {
4148
+ this.control.propertiesActivators.appendChild(container);
4149
+ });
4150
+ }
3786
4151
  }
3787
4152
  }
3788
4153
  refreshEditors() {
3789
- while (this.control.childrenSlot.firstChild) {
3790
- this.control.childrenSlot.removeChild(this.control.childrenSlot.firstChild);
3791
- }
4154
+ this.control.childrenSlot.replaceChildren();
3792
4155
  this.instance.children.forEach((child) => {
3793
4156
  const optIn = this.theme.getCheckboxControl({
3794
4157
  id: child.path + "-opt-in",
@@ -3822,11 +4185,32 @@ class EditorObject extends Editor {
3822
4185
  }
3823
4186
  });
3824
4187
  }
4188
+ refreshLegendWarning() {
4189
+ if (!this.control.legendText) return;
4190
+ const navWarning = getSchemaXOption(this.instance.schema, "navWarning") ?? true;
4191
+ const hasErrors = navWarning && this.instance.hasNestedValidationErrors();
4192
+ const existing = this.control.legendText.querySelector(".jedi-legend-warning");
4193
+ if (existing) existing.parentNode.removeChild(existing);
4194
+ if (hasErrors) {
4195
+ const warning = document.createElement("span");
4196
+ warning.classList.add("jedi-legend-warning");
4197
+ warning.textContent = "⚠";
4198
+ const navWarningMessage = getSchemaXOption(this.instance.schema, "navWarningMessage");
4199
+ if (navWarningMessage) warning.setAttribute("title", navWarningMessage);
4200
+ this.theme.styleLegendWarning(warning);
4201
+ this.control.legendText.appendChild(warning);
4202
+ }
4203
+ }
4204
+ showValidationErrors(errors, force = false) {
4205
+ super.showValidationErrors(errors, force);
4206
+ this.refreshLegendWarning();
4207
+ }
3825
4208
  refreshUI() {
3826
4209
  super.refreshUI();
3827
4210
  this.refreshPropertiesSlot();
3828
4211
  this.refreshEditors();
3829
4212
  this.refreshJsonData();
4213
+ this.refreshLegendWarning();
3830
4214
  }
3831
4215
  }
3832
4216
  class EditorObjectGrid extends EditorObject {
@@ -3877,6 +4261,29 @@ class EditorObjectCategories extends EditorObject {
3877
4261
  super.init();
3878
4262
  this.activeCategoryName = null;
3879
4263
  }
4264
+ navigateTo(path) {
4265
+ const nextChildPath = this.getNextChildPath(path);
4266
+ if (nextChildPath) {
4267
+ const child = this.instance.children.find((c) => c.path === nextChildPath);
4268
+ if (child) {
4269
+ const defaultLabel = getSchemaXOption(this.instance.schema, "categoriesDefaultLabel") ?? "Basic";
4270
+ const childSchemaType = getSchemaType(child.schema);
4271
+ const xCategory = getSchemaXOption(child.schema, "category");
4272
+ let categoryName;
4273
+ if (isSet(xCategory)) {
4274
+ categoryName = xCategory;
4275
+ } else if (childSchemaType === "object" || childSchemaType === "array") {
4276
+ const schemaTitle = getSchemaTitle(child.schema);
4277
+ categoryName = isSet(schemaTitle) ? schemaTitle : child.getKey();
4278
+ } else {
4279
+ categoryName = defaultLabel;
4280
+ }
4281
+ this.activeCategoryName = categoryName;
4282
+ this.refreshUI();
4283
+ }
4284
+ }
4285
+ super.navigateTo(path);
4286
+ }
3880
4287
  refreshEditors() {
3881
4288
  while (this.control.childrenSlot.firstChild) {
3882
4289
  this.control.childrenSlot.removeChild(this.control.childrenSlot.lastChild);
@@ -3981,6 +4388,17 @@ class EditorObjectNav extends EditorObject {
3981
4388
  this.activeTabIndex = visibleIndices[0] ?? 0;
3982
4389
  }
3983
4390
  }
4391
+ navigateTo(path) {
4392
+ const nextChildPath = this.getNextChildPath(path);
4393
+ if (nextChildPath) {
4394
+ const childIndex = this.instance.children.findIndex((c) => c.path === nextChildPath);
4395
+ if (childIndex !== -1) {
4396
+ this.activeTabIndex = childIndex;
4397
+ this.refreshUI();
4398
+ }
4399
+ }
4400
+ super.navigateTo(path);
4401
+ }
3984
4402
  refreshEditors() {
3985
4403
  while (this.control.childrenSlot.firstChild) {
3986
4404
  this.control.childrenSlot.removeChild(this.control.childrenSlot.lastChild);
@@ -4105,10 +4523,19 @@ class EditorArray extends Editor {
4105
4523
  });
4106
4524
  const btnGroup = this.theme.getBtnGroup();
4107
4525
  deleteBtn.addEventListener("click", () => {
4108
- const confirmDeletion = window.confirm(this.instance.jedison.translator.translate("arrayConfirmDelete"));
4109
- if (confirmDeletion) {
4526
+ const schemaConfirm = getSchemaXOption(this.instance.schema, "arrayDeleteConfirm");
4527
+ const globalConfirm = this.instance.jedison.options.arrayDeleteConfirm;
4528
+ const shouldConfirm = isSet(schemaConfirm) ? schemaConfirm : globalConfirm;
4529
+ const doDelete = () => {
4110
4530
  this.activeItemIndex = clamp(index2 - 1, 0, this.instance.value.length - 1);
4111
4531
  this.instance.deleteItem(index2, "user");
4532
+ };
4533
+ if (shouldConfirm) {
4534
+ if (window.confirm(this.instance.jedison.translator.translate("arrayConfirmDelete"))) {
4535
+ doDelete();
4536
+ }
4537
+ } else {
4538
+ doDelete();
4112
4539
  }
4113
4540
  });
4114
4541
  moveUpBtn.addEventListener("click", () => {
@@ -4201,6 +4628,27 @@ class EditorArray extends Editor {
4201
4628
  });
4202
4629
  this.refreshAddBtn();
4203
4630
  this.refreshJsonData();
4631
+ this.refreshLegendWarning();
4632
+ }
4633
+ refreshLegendWarning() {
4634
+ if (!this.control.legendText) return;
4635
+ const navWarning = getSchemaXOption(this.instance.schema, "navWarning") ?? true;
4636
+ const hasErrors = navWarning && this.instance.hasNestedValidationErrors();
4637
+ const existing = this.control.legendText.querySelector(".jedi-legend-warning");
4638
+ if (existing) existing.parentNode.removeChild(existing);
4639
+ if (hasErrors) {
4640
+ const warning = document.createElement("span");
4641
+ warning.classList.add("jedi-legend-warning");
4642
+ warning.textContent = "⚠";
4643
+ const navWarningMessage = getSchemaXOption(this.instance.schema, "navWarningMessage");
4644
+ if (navWarningMessage) warning.setAttribute("title", navWarningMessage);
4645
+ this.theme.styleLegendWarning(warning);
4646
+ this.control.legendText.appendChild(warning);
4647
+ }
4648
+ }
4649
+ showValidationErrors(errors, force = false) {
4650
+ super.showValidationErrors(errors, force);
4651
+ this.refreshLegendWarning();
4204
4652
  }
4205
4653
  }
4206
4654
  class EditorArrayTuple extends EditorArray {
@@ -4275,20 +4723,22 @@ class EditorArrayTable extends EditorArray {
4275
4723
  if (this.instance.children.length) {
4276
4724
  const schemaItems = getSchemaItems(this.instance.schema);
4277
4725
  const thTitle = this.theme.getTableHeader();
4278
- if (schemaItems.title) {
4279
- const fakeLabel = this.theme.getFakeLabel({
4280
- content: schemaItems.title
4281
- });
4282
- thTitle.appendChild(fakeLabel.label);
4283
- }
4284
- const schemaXInfo = getSchemaXOption(schemaItems, "info");
4285
- if (isSet(schemaXInfo)) {
4286
- const infoContent = this.getInfo(schemaItems);
4287
- const info = this.theme.getInfo(infoContent);
4288
- if (schemaXInfo.variant === "modal") {
4289
- this.theme.infoAsModal(info, this.getIdFromPath(this.instance.path), infoContent);
4726
+ if (schemaItems) {
4727
+ if (schemaItems.title) {
4728
+ const fakeLabel = this.theme.getFakeLabel({
4729
+ content: schemaItems.title
4730
+ });
4731
+ thTitle.appendChild(fakeLabel.label);
4732
+ }
4733
+ const schemaXInfo = getSchemaXOption(schemaItems, "info");
4734
+ if (isSet(schemaXInfo)) {
4735
+ const infoContent = this.getInfo(schemaItems);
4736
+ const info = this.theme.getInfo(infoContent);
4737
+ if (schemaXInfo.variant === "modal") {
4738
+ this.theme.infoAsModal(info, this.getIdFromPath(this.instance.path), infoContent);
4739
+ }
4740
+ thTitle.appendChild(info.container);
4290
4741
  }
4291
- thTitle.appendChild(info.container);
4292
4742
  }
4293
4743
  table.thead.appendChild(thTitle);
4294
4744
  }
@@ -4508,6 +4958,45 @@ class EditorArrayChoices extends Editor {
4508
4958
  const hasValidItemType = isSet(schemaItems) && isSet(schemaItemsType) && (validTypes.includes(schemaItemsType) || isArray(schemaItemsType) && schemaItemsType.some((type2) => validTypes.includes(type2)));
4509
4959
  return hasChoicesFormat && choicesInstalled && isArrayType && isUniqueItems && hasTypes && hasValidItemType;
4510
4960
  }
4961
+ init() {
4962
+ super.init();
4963
+ this.setupEnumSource();
4964
+ }
4965
+ setupEnumSource() {
4966
+ const enumSourceRaw = getSchemaXOption(this.instance.schema, "enumSource");
4967
+ if (!isSet(enumSourceRaw)) return;
4968
+ const enumSource = resolveInstancePath(this.instance.path, enumSourceRaw);
4969
+ const src = this.instance.jedison.getInstance(enumSource);
4970
+ if (src) this.enumSourceValues = src.getValue();
4971
+ this.instance.jedison.watch(enumSource, () => {
4972
+ if (!this.control) return;
4973
+ const s = this.instance.jedison.getInstance(enumSource);
4974
+ if (s) {
4975
+ this.enumSourceValues = s.getValue();
4976
+ this.refreshOptions();
4977
+ }
4978
+ });
4979
+ }
4980
+ getEnumSourceValues() {
4981
+ if (this.enumSourceValues !== void 0) {
4982
+ if (isArray(this.enumSourceValues)) return this.enumSourceValues;
4983
+ if (isObject(this.enumSourceValues)) return Object.keys(this.enumSourceValues);
4984
+ return [];
4985
+ }
4986
+ return this.instance.schema.items && this.instance.schema.items.enum || [];
4987
+ }
4988
+ refreshOptions() {
4989
+ if (!this.choicesInstance) return;
4990
+ const values = this.getEnumSourceValues();
4991
+ const currentValue = this.instance.getValue();
4992
+ const itemEnumTitles = getSchemaXOption(this.instance.schema.items || {}, "enumTitles") || [];
4993
+ const choices = values.map((item, index2) => ({
4994
+ value: item,
4995
+ label: itemEnumTitles[index2] || item,
4996
+ selected: isArray(currentValue) && currentValue.includes(item)
4997
+ }));
4998
+ this.choicesInstance.setChoices(choices, "value", "label", true);
4999
+ }
4511
5000
  build() {
4512
5001
  this.control = this.theme.getSelectControl({
4513
5002
  title: this.getTitle(),
@@ -4522,8 +5011,8 @@ class EditorArrayChoices extends Editor {
4522
5011
  this.control.input.setAttribute("multiple", "");
4523
5012
  try {
4524
5013
  const value = this.instance.getValue();
4525
- const itemEnum = this.instance.schema.items.enum ?? [];
4526
- const itemEnumTitles = getSchemaXOption(this.instance.schema.items, "enumTitles") ?? this.instance.getValue();
5014
+ const itemEnum = this.getEnumSourceValues();
5015
+ const itemEnumTitles = getSchemaXOption(this.instance.schema.items || {}, "enumTitles") || [];
4527
5016
  const choicesOptions = getSchemaXOption(this.instance.schema, "choicesOptions") ?? {};
4528
5017
  if (this.choicesInstance) {
4529
5018
  this.choicesInstance.destroy();
@@ -4580,6 +5069,17 @@ class EditorArrayNav extends EditorArray {
4580
5069
  const hasNavFormat = regex.test(format2);
4581
5070
  return getSchemaType(schema) === "array" && hasNavFormat;
4582
5071
  }
5072
+ navigateTo(path) {
5073
+ const nextChildPath = this.getNextChildPath(path);
5074
+ if (nextChildPath) {
5075
+ const childIndex = this.instance.children.findIndex((c) => c.path === nextChildPath);
5076
+ if (childIndex !== -1) {
5077
+ this.activeItemIndex = childIndex;
5078
+ this.refreshUI();
5079
+ }
5080
+ }
5081
+ super.navigateTo(path);
5082
+ }
4583
5083
  addEventListeners() {
4584
5084
  this.control.addBtn.addEventListener("click", () => {
4585
5085
  this.activeItemIndex = this.instance.value.length;
@@ -4678,6 +5178,7 @@ class EditorMultiple extends Editor {
4678
5178
  }
4679
5179
  build() {
4680
5180
  this.switcherInput = getSchemaXOption(this.instance.schema, "switcherInput") ?? this.instance.jedison.options.switcherInput;
5181
+ this.embedSwitcher = getSchemaXOption(this.instance.schema, "embedSwitcher") ?? this.instance.jedison.options.embedSwitcher;
4681
5182
  this.control = this.theme.getMultipleControl({
4682
5183
  titleHidden: getSchemaXOption(this.instance.schema, "titleHidden"),
4683
5184
  id: this.getIdFromPath(this.instance.path),
@@ -4686,6 +5187,9 @@ class EditorMultiple extends Editor {
4686
5187
  switcher: this.switcherInput,
4687
5188
  readOnly: this.instance.isReadOnly()
4688
5189
  });
5190
+ if (this.embedSwitcher) {
5191
+ this.control.header.style.display = "none";
5192
+ }
4689
5193
  }
4690
5194
  adaptForTable(td) {
4691
5195
  this.theme.adaptForTableMultipleControl(this.control, td);
@@ -4710,6 +5214,17 @@ class EditorMultiple extends Editor {
4710
5214
  this.refreshDisabledState();
4711
5215
  this.control.childrenSlot.innerHTML = "";
4712
5216
  this.control.childrenSlot.appendChild(this.instance.activeInstance.ui.control.container);
5217
+ if (this.embedSwitcher) {
5218
+ const slot = this.instance.activeInstance.ui.control.switcherSlot;
5219
+ if (slot) {
5220
+ slot.innerHTML = "";
5221
+ slot.appendChild(this.control.switcher.container);
5222
+ this.control.header.style.display = "none";
5223
+ } else {
5224
+ this.control.header.style.display = "";
5225
+ this.control.header.appendChild(this.control.switcher.container);
5226
+ }
5227
+ }
4713
5228
  if (this.switcherInput === "select") {
4714
5229
  this.control.switcher.input.value = this.instance.index;
4715
5230
  }
@@ -5079,6 +5594,38 @@ class EditorNumberRaty extends EditorNumber {
5079
5594
  this.raty.score(this.instance.getValue());
5080
5595
  }
5081
5596
  }
5597
+ class EditorAnyJson extends Editor {
5598
+ static resolves(schema) {
5599
+ return getSchemaXOption(schema, "format") === "json";
5600
+ }
5601
+ build() {
5602
+ this.control = this.theme.getTextareaControl({
5603
+ title: this.getTitle(),
5604
+ description: this.getDescription(),
5605
+ id: this.getIdFromPath(this.instance.path),
5606
+ titleIconClass: getSchemaXOption(this.instance.schema, "titleIconClass"),
5607
+ titleHidden: getSchemaXOption(this.instance.schema, "titleHidden"),
5608
+ info: this.getInfo()
5609
+ });
5610
+ this.jsonErrorEl = document.createElement("div");
5611
+ this.jsonErrorEl.style.color = "red";
5612
+ this.control.container.appendChild(this.jsonErrorEl);
5613
+ }
5614
+ addEventListeners() {
5615
+ this.control.input.addEventListener("change", () => {
5616
+ try {
5617
+ const parsed = JSON.parse(this.control.input.value);
5618
+ this.jsonErrorEl.textContent = "";
5619
+ this.instance.setValue(parsed, true, "user");
5620
+ } catch (e) {
5621
+ this.jsonErrorEl.textContent = e.message;
5622
+ }
5623
+ });
5624
+ }
5625
+ refreshUI() {
5626
+ this.control.input.value = JSON.stringify(this.instance.getValue(), null, 2);
5627
+ }
5628
+ }
5082
5629
  class EditorArrayCheckboxes extends Editor {
5083
5630
  static resolves(schema) {
5084
5631
  const schemaType = getSchemaType(schema);
@@ -5088,22 +5635,93 @@ class EditorArrayCheckboxes extends Editor {
5088
5635
  const isUniqueItems = getSchemaUniqueItems(schema) === true;
5089
5636
  const hasEnum = isSet(schemaItems) && isSet(getSchemaEnum(schema.items));
5090
5637
  const hasTypes = isSet(schemaItems) && isSet(schemaItemsType);
5638
+ const hasEnumSource = isSet(getSchemaXOption(schema, "enumSource"));
5091
5639
  const validTypes = ["string", "number", "integer"];
5092
5640
  const hasValidItemType = isSet(schemaItems) && isSet(schemaItemsType) && (validTypes.includes(schemaItemsType) || isArray(schemaItemsType) && schemaItemsType.some((type2) => validTypes.includes(type2)));
5093
- return isArrayType && isUniqueItems && hasEnum && hasTypes && hasValidItemType;
5641
+ return isArrayType && isUniqueItems && (hasEnumSource || hasEnum && hasTypes && hasValidItemType);
5642
+ }
5643
+ init() {
5644
+ super.init();
5645
+ this.setupEnumSource();
5646
+ }
5647
+ setupEnumSource() {
5648
+ const enumSourceRaw = getSchemaXOption(this.instance.schema, "enumSource");
5649
+ if (!isSet(enumSourceRaw)) return;
5650
+ const enumSource = resolveInstancePath(this.instance.path, enumSourceRaw);
5651
+ const src = this.instance.jedison.getInstance(enumSource);
5652
+ if (src) this.enumSourceValues = src.getValue();
5653
+ this.instance.jedison.watch(enumSource, () => {
5654
+ if (!this.control) return;
5655
+ const s = this.instance.jedison.getInstance(enumSource);
5656
+ if (s) {
5657
+ this.enumSourceValues = s.getValue();
5658
+ this.refreshOptions();
5659
+ }
5660
+ });
5661
+ }
5662
+ getEnumSourceValues() {
5663
+ if (this.enumSourceValues !== void 0) {
5664
+ if (isArray(this.enumSourceValues)) return this.enumSourceValues;
5665
+ if (isObject(this.enumSourceValues)) return Object.keys(this.enumSourceValues);
5666
+ return [];
5667
+ }
5668
+ return getSchemaEnum(this.instance.schema.items) || [];
5094
5669
  }
5095
5670
  build() {
5671
+ const values = this.getEnumSourceValues();
5672
+ const schemaItems = this.instance.schema.items || {};
5673
+ const titles = getSchemaXOption(schemaItems, "enumTitles") || values;
5096
5674
  this.control = this.theme.getCheckboxesControl({
5097
5675
  title: this.getTitle(),
5098
5676
  description: this.getDescription(),
5099
- values: getSchemaEnum(this.instance.schema.items),
5100
- titles: getSchemaXOption(this.instance.schema.items, "enumTitles") || getSchemaEnum(this.instance.schema.items),
5677
+ values,
5678
+ titles,
5101
5679
  id: this.getIdFromPath(this.instance.path),
5102
5680
  titleHidden: getSchemaXOption(this.instance.schema, "titleHidden"),
5103
5681
  inline: getSchemaXOption(this.instance.schema, "format") === "checkboxes-inline",
5104
5682
  info: this.getInfo()
5105
5683
  });
5106
5684
  }
5685
+ refreshOptions() {
5686
+ const values = this.getEnumSourceValues();
5687
+ const schemaItems = this.instance.schema.items || {};
5688
+ const titles = getSchemaXOption(schemaItems, "enumTitles") || values;
5689
+ const id = this.getIdFromPath(this.instance.path);
5690
+ const messagesId = id + "-messages";
5691
+ const descriptionId = id + "-description";
5692
+ const describedBy = messagesId + " " + descriptionId;
5693
+ this.control.checkboxControls.forEach((cc) => {
5694
+ if (cc.parentNode) cc.parentNode.removeChild(cc);
5695
+ });
5696
+ this.control.checkboxes = [];
5697
+ this.control.labels = [];
5698
+ this.control.checkboxControls = [];
5699
+ this.control.labelTexts = [];
5700
+ values.forEach((value, index2) => {
5701
+ const checkboxId = id + "-" + index2;
5702
+ const checkboxControl = document.createElement("div");
5703
+ const checkbox = document.createElement("input");
5704
+ const label = document.createElement("label");
5705
+ const labelText = document.createElement("span");
5706
+ checkbox.setAttribute("type", "checkbox");
5707
+ checkbox.setAttribute("id", checkboxId);
5708
+ checkbox.setAttribute("name", id);
5709
+ checkbox.setAttribute("value", value);
5710
+ checkbox.setAttribute("aria-describedby", describedBy);
5711
+ label.setAttribute("for", checkboxId);
5712
+ labelText.textContent = titles && titles[index2] !== void 0 ? titles[index2] : value;
5713
+ checkboxControl.appendChild(checkbox);
5714
+ checkboxControl.appendChild(label);
5715
+ label.appendChild(labelText);
5716
+ this.control.checkboxes.push(checkbox);
5717
+ this.control.labels.push(label);
5718
+ this.control.labelTexts.push(labelText);
5719
+ this.control.checkboxControls.push(checkboxControl);
5720
+ this.control.fieldset.insertBefore(checkboxControl, this.control.description);
5721
+ });
5722
+ this.addEventListeners();
5723
+ this.refreshUI();
5724
+ }
5107
5725
  adaptForTable(td) {
5108
5726
  this.theme.adaptForTableCheckboxesControl(this.control, td);
5109
5727
  }
@@ -5136,6 +5754,15 @@ class EditorArrayCheckboxes extends Editor {
5136
5754
  checkbox.checked = value.includes(checkbox.value);
5137
5755
  });
5138
5756
  }
5757
+ setAriaInvalid(invalid) {
5758
+ this.control.checkboxes.forEach((checkbox) => {
5759
+ if (invalid) {
5760
+ checkbox.setAttribute("aria-invalid", "true");
5761
+ } else {
5762
+ checkbox.removeAttribute("aria-invalid");
5763
+ }
5764
+ });
5765
+ }
5139
5766
  }
5140
5767
  class EditorNumberRange extends EditorNumber {
5141
5768
  static resolves(schema) {
@@ -5310,6 +5937,7 @@ class UiResolver {
5310
5937
  EditorNumberInputNullable,
5311
5938
  EditorMultiple,
5312
5939
  EditorIfThenElse,
5940
+ EditorAnyJson,
5313
5941
  EditorRadios,
5314
5942
  EditorBooleanCheckbox,
5315
5943
  EditorBooleanSelect,
@@ -5648,14 +6276,17 @@ class Jedison extends EventEmitter {
5648
6276
  btnContents: true,
5649
6277
  btnIcons: true,
5650
6278
  arrayDelete: true,
6279
+ arrayDeleteConfirm: true,
5651
6280
  arrayMove: true,
5652
6281
  arrayAdd: true,
6282
+ objectAdd: true,
5653
6283
  arrayButtonsPosition: "left",
5654
6284
  startCollapsed: false,
5655
6285
  deactivateNonRequired: false,
5656
6286
  schema: {},
5657
6287
  showErrors: "change",
5658
6288
  switcherInput: "select",
6289
+ embedSwitcher: false,
5659
6290
  data: void 0,
5660
6291
  assertFormat: false,
5661
6292
  customEditors: [],
@@ -5680,6 +6311,7 @@ class Jedison extends EventEmitter {
5680
6311
  enforceMinItems: true,
5681
6312
  enforceMaxItems: true,
5682
6313
  enforceEnum: true,
6314
+ subErrors: false,
5683
6315
  debug: false
5684
6316
  }, options);
5685
6317
  this.rootName = "#";
@@ -5747,7 +6379,8 @@ class Jedison extends EventEmitter {
5747
6379
  refParser: this.refParser,
5748
6380
  assertFormat: this.options.assertFormat,
5749
6381
  translator: this.translator,
5750
- constraints: this.options.constraints
6382
+ constraints: this.options.constraints,
6383
+ subErrors: this.options.subErrors
5751
6384
  });
5752
6385
  this.root = this.createInstance({
5753
6386
  jedison: this,
@@ -6010,6 +6643,14 @@ class Jedison extends EventEmitter {
6010
6643
  getInstance(path) {
6011
6644
  return this.instances.get(path);
6012
6645
  }
6646
+ /**
6647
+ * Navigates to a specific instance by path, activating any ancestor nav/categories tabs as needed.
6648
+ * @param {string} path - The instance path (e.g. '#/address/street')
6649
+ */
6650
+ navigateTo(path) {
6651
+ if (!this.isEditor) return;
6652
+ this.root.ui.navigateTo(path);
6653
+ }
6013
6654
  /**
6014
6655
  * Disables the root instance and it's children user interfaces
6015
6656
  */
@@ -6299,6 +6940,8 @@ class Theme {
6299
6940
  const dummyInputId = "legend-dummy-input-" + config.id;
6300
6941
  left.classList.add("jedi-editor-legend-left");
6301
6942
  right.classList.add("jedi-editor-legend-right");
6943
+ right.style.display = "flex";
6944
+ right.style.alignItems = "center";
6302
6945
  legend.classList.add("jedi-editor-legend");
6303
6946
  legend.style.fontSize = "inherit";
6304
6947
  legend.setAttribute("aria-labelledby", legendLabelId);
@@ -6514,6 +7157,18 @@ class Theme {
6514
7157
  });
6515
7158
  return toggle;
6516
7159
  }
7160
+ getQuickAddPropertyToggle(config) {
7161
+ const toggle = this.getButton(config);
7162
+ toggle.classList.add("jedi-quick-add-property-toggle");
7163
+ toggle.addEventListener("click", () => {
7164
+ if (config.propertiesContainer.open) {
7165
+ config.propertiesContainer.close();
7166
+ } else {
7167
+ config.propertiesContainer.showModal();
7168
+ }
7169
+ });
7170
+ return toggle;
7171
+ }
6517
7172
  /**
6518
7173
  * Container that will collapse and expand to show and hide it contents
6519
7174
  */
@@ -6576,6 +7231,17 @@ class Theme {
6576
7231
  });
6577
7232
  return html;
6578
7233
  }
7234
+ getQuickAddPropertySlot(config) {
7235
+ const html = document.createElement("dialog");
7236
+ html.classList.add("jedi-quick-add-property-slot");
7237
+ html.setAttribute("id", config.id);
7238
+ window.addEventListener("click", (event) => {
7239
+ if (event.target === html) {
7240
+ html.close();
7241
+ }
7242
+ });
7243
+ return html;
7244
+ }
6579
7245
  /**
6580
7246
  * Container for properties editing elements like property activators
6581
7247
  */
@@ -6928,17 +7594,25 @@ class Theme {
6928
7594
  collapse,
6929
7595
  startCollapsed: config.startCollapsed
6930
7596
  });
6931
- const addPropertyControl = this.getInputControl({
7597
+ const quickAddPropertyContainer = this.getQuickAddPropertySlot({
7598
+ id: "quick-add-property-slot-" + config.id
7599
+ });
7600
+ const quickAddPropertyControl = this.getInputControl({
6932
7601
  type: "text",
6933
- id: "jedi-add-property-input-" + config.id,
7602
+ id: "jedi-quick-add-property-input-" + config.id,
6934
7603
  title: config.addPropertyContent
6935
7604
  });
6936
- const addPropertyBtn = this.getAddPropertyButton({
7605
+ const quickAddPropertyBtn = this.getAddPropertyButton({
6937
7606
  content: config.addPropertyContent,
6938
7607
  icon: "add"
6939
7608
  });
7609
+ const quickAddPropertyToggle = this.getQuickAddPropertyToggle({
7610
+ content: config.addPropertyContent,
7611
+ icon: "add",
7612
+ propertiesContainer: quickAddPropertyContainer
7613
+ });
6940
7614
  const fieldset = this.getFieldset();
6941
- const { legend, infoContainer, legendText } = this.getLegend({
7615
+ const { legend, infoContainer, legendText, right } = this.getLegend({
6942
7616
  content: config.title,
6943
7617
  id: config.id,
6944
7618
  titleHidden: config.titleHidden
@@ -6946,9 +7620,13 @@ class Theme {
6946
7620
  if (((_a = config == null ? void 0 : config.info) == null ? void 0 : _a.variant) === "modal") {
6947
7621
  this.infoAsModal(info, config.id, config.info);
6948
7622
  }
6949
- addPropertyBtn.classList.add("jedi-object-add");
6950
7623
  container.appendChild(fieldset);
6951
7624
  container.appendChild(propertiesContainer);
7625
+ container.appendChild(quickAddPropertyContainer);
7626
+ if (config.addProperty) {
7627
+ quickAddPropertyContainer.appendChild(quickAddPropertyControl.container);
7628
+ quickAddPropertyContainer.appendChild(quickAddPropertyBtn);
7629
+ }
6952
7630
  if (config.editJsonData) {
6953
7631
  container.appendChild(jsonData.dialog);
6954
7632
  }
@@ -6962,18 +7640,19 @@ class Theme {
6962
7640
  body.appendChild(description);
6963
7641
  }
6964
7642
  body.appendChild(messages);
7643
+ const switcherSlot = document.createElement("div");
7644
+ switcherSlot.classList.add("jedi-switcher-slot");
6965
7645
  if (config.readOnly === false) {
6966
- legend.appendChild(actions);
7646
+ right.appendChild(switcherSlot);
7647
+ right.appendChild(actions);
6967
7648
  }
6968
7649
  body.appendChild(childrenSlot);
6969
- if (config.addProperty) {
6970
- propertiesContainer.appendChild(addPropertyControl.container);
6971
- propertiesContainer.appendChild(addPropertyBtn);
6972
- propertiesContainer.appendChild(document.createElement("hr"));
6973
- }
6974
7650
  if (config.editJsonData) {
6975
7651
  actions.appendChild(jsonData.toggle);
6976
7652
  }
7653
+ if (config.addProperty) {
7654
+ actions.appendChild(quickAddPropertyToggle);
7655
+ }
6977
7656
  if (config.enablePropertiesToggle) {
6978
7657
  actions.appendChild(propertiesToggle);
6979
7658
  propertiesContainer.appendChild(ariaLive);
@@ -6994,13 +7673,17 @@ class Theme {
6994
7673
  propertiesToggle,
6995
7674
  jsonData,
6996
7675
  propertiesContainer,
6997
- addPropertyControl,
6998
- addPropertyBtn,
7676
+ quickAddPropertyContainer,
7677
+ quickAddPropertyControl,
7678
+ quickAddPropertyBtn,
7679
+ quickAddPropertyToggle,
6999
7680
  ariaLive,
7000
7681
  propertiesActivators,
7001
7682
  legend,
7002
7683
  legendText,
7003
- infoContainer
7684
+ infoContainer,
7685
+ right,
7686
+ switcherSlot
7004
7687
  };
7005
7688
  }
7006
7689
  /**
@@ -7021,7 +7704,7 @@ class Theme {
7021
7704
  });
7022
7705
  const fieldset = this.getFieldset();
7023
7706
  const info = this.getInfo(config.info);
7024
- const { legend, legendText, infoContainer } = this.getLegend({
7707
+ const { legend, legendText, infoContainer, right } = this.getLegend({
7025
7708
  content: config.title,
7026
7709
  id: config.id,
7027
7710
  titleHidden: config.titleHidden
@@ -7061,7 +7744,12 @@ class Theme {
7061
7744
  body.appendChild(description);
7062
7745
  }
7063
7746
  body.appendChild(messages);
7064
- legend.appendChild(actions);
7747
+ const switcherSlot = document.createElement("div");
7748
+ switcherSlot.classList.add("jedi-switcher-slot");
7749
+ if (config.readOnly === false) {
7750
+ right.appendChild(switcherSlot);
7751
+ right.appendChild(actions);
7752
+ }
7065
7753
  actions.appendChild(btnGroup);
7066
7754
  if (config.editJsonData) {
7067
7755
  btnGroup.appendChild(jsonData.toggle);
@@ -7085,7 +7773,8 @@ class Theme {
7085
7773
  addBtn,
7086
7774
  jsonData,
7087
7775
  legend,
7088
- legendText
7776
+ legendText,
7777
+ switcherSlot
7089
7778
  };
7090
7779
  }
7091
7780
  getArrayItem(config = {}) {
@@ -7121,8 +7810,10 @@ class Theme {
7121
7810
  const messages = this.getMessagesSlot();
7122
7811
  const childrenSlot = this.getChildrenSlot();
7123
7812
  const randomId = generateRandomID(5);
7813
+ const knownSwitchers = ["select", "radios", "radios-inline"];
7814
+ const switcherType = knownSwitchers.includes(config.switcher) ? config.switcher : "select";
7124
7815
  let switcher;
7125
- if (config.switcher === "select") {
7816
+ if (switcherType === "select") {
7126
7817
  switcher = this.getSwitcherSelect({
7127
7818
  values: config.switcherOptionValues,
7128
7819
  titles: config.switcherOptionsLabels,
@@ -7130,10 +7821,11 @@ class Theme {
7130
7821
  id: config.id + "-switcher-" + randomId,
7131
7822
  label: config.id + "-switcher-" + randomId,
7132
7823
  titleHidden: true,
7133
- readOnly: config.readOnly
7824
+ readOnly: config.readOnly,
7825
+ noSpacing: true
7134
7826
  });
7135
7827
  }
7136
- if (config.switcher === "radios" || config.switcher === "radios-inline") {
7828
+ if (switcherType === "radios" || switcherType === "radios-inline") {
7137
7829
  switcher = this.getSwitcherRadios({
7138
7830
  values: config.switcherOptionValues,
7139
7831
  titles: config.switcherOptionsLabels,
@@ -7142,7 +7834,8 @@ class Theme {
7142
7834
  label: config.id + "-switcher-" + randomId,
7143
7835
  titleHidden: true,
7144
7836
  readOnly: config.readOnly,
7145
- inline: config.switcher === "radios-inline"
7837
+ inline: switcherType === "radios-inline",
7838
+ noSpacing: true
7146
7839
  });
7147
7840
  }
7148
7841
  switcher.container.classList.add("jedi-switcher");
@@ -7680,6 +8373,8 @@ class Theme {
7680
8373
  tabList.classList.add("jedi-nav-list");
7681
8374
  return tabList;
7682
8375
  }
8376
+ styleLegendWarning(span) {
8377
+ }
7683
8378
  /**
7684
8379
  * A Tab is a wrapper for content
7685
8380
  */
@@ -7991,7 +8686,9 @@ class ThemeBootstrap3 extends Theme {
7991
8686
  getSelectControl(config) {
7992
8687
  const control = super.getSelectControl(config);
7993
8688
  const { container, input, label } = control;
7994
- container.classList.add("form-group");
8689
+ if (!config.noSpacing) {
8690
+ container.classList.add("form-group");
8691
+ }
7995
8692
  input.classList.add("form-control");
7996
8693
  if (config.titleHidden) {
7997
8694
  this.visuallyHidden(label);
@@ -8091,10 +8788,8 @@ class ThemeBootstrap3 extends Theme {
8091
8788
  setTabPaneAttributes(element, active, id) {
8092
8789
  super.setTabPaneAttributes(element, active, id);
8093
8790
  element.classList.add("tab-pane");
8094
- if (active) {
8095
- element.classList.add("in");
8096
- element.classList.add("active");
8097
- }
8791
+ element.classList.toggle("in", active);
8792
+ element.classList.toggle("active", active);
8098
8793
  }
8099
8794
  infoAsModal(info, id, config = {}) {
8100
8795
  const modal = document.createElement("div");
@@ -8291,7 +8986,9 @@ class ThemeBootstrap4 extends Theme {
8291
8986
  getRadiosControl(config) {
8292
8987
  const control = super.getRadiosControl(config);
8293
8988
  const { container, fieldset, radios, labels, labelTexts, radioControls, description, messages } = control;
8294
- container.classList.add("form-group");
8989
+ if (!config.noSpacing) {
8990
+ container.classList.add("form-group");
8991
+ }
8295
8992
  radioControls.forEach((radioControl, index2) => {
8296
8993
  radioControl.classList.add("form-check");
8297
8994
  radios[index2].classList.add("form-check-input");
@@ -8363,7 +9060,9 @@ class ThemeBootstrap4 extends Theme {
8363
9060
  getSelectControl(config) {
8364
9061
  const control = super.getSelectControl(config);
8365
9062
  const { container, input } = control;
8366
- container.classList.add("form-group");
9063
+ if (!config.noSpacing) {
9064
+ container.classList.add("form-group");
9065
+ }
8367
9066
  input.classList.add("form-control");
8368
9067
  return control;
8369
9068
  }
@@ -8468,9 +9167,7 @@ class ThemeBootstrap4 extends Theme {
8468
9167
  setTabPaneAttributes(element, active, id) {
8469
9168
  super.setTabPaneAttributes(element, active, id);
8470
9169
  element.classList.add("tab-pane");
8471
- if (active) {
8472
- element.classList.add("active");
8473
- }
9170
+ element.classList.toggle("active", active);
8474
9171
  }
8475
9172
  infoAsModal(info, id, config = {}) {
8476
9173
  const modal = document.createElement("div");
@@ -8572,6 +9269,9 @@ class ThemeBootstrap5 extends Theme {
8572
9269
  legend.classList.add("py-2");
8573
9270
  return superLegend;
8574
9271
  }
9272
+ styleLegendWarning(span) {
9273
+ span.classList.add("ms-1");
9274
+ }
8575
9275
  getLabel(config) {
8576
9276
  const labelObj = super.getLabel(config);
8577
9277
  if (labelObj.icon.classList) {
@@ -8670,7 +9370,9 @@ class ThemeBootstrap5 extends Theme {
8670
9370
  getRadiosControl(config) {
8671
9371
  const control = super.getRadiosControl(config);
8672
9372
  const { container, fieldset, radios, labels, labelTexts, radioControls, description, messages } = control;
8673
- container.classList.add("mb-3");
9373
+ if (!config.noSpacing) {
9374
+ container.classList.add("mb-3");
9375
+ }
8674
9376
  radioControls.forEach((radioControl, index2) => {
8675
9377
  radioControl.classList.add("form-check");
8676
9378
  radios[index2].classList.add("form-check-input");
@@ -8735,7 +9437,9 @@ class ThemeBootstrap5 extends Theme {
8735
9437
  getSelectControl(config) {
8736
9438
  const control = super.getSelectControl(config);
8737
9439
  const { container, input } = control;
8738
- container.classList.add("mb-3");
9440
+ if (!config.noSpacing) {
9441
+ container.classList.add("mb-3");
9442
+ }
8739
9443
  input.classList.add("form-select");
8740
9444
  return control;
8741
9445
  }
@@ -8837,9 +9541,7 @@ class ThemeBootstrap5 extends Theme {
8837
9541
  setTabPaneAttributes(element, active, id) {
8838
9542
  super.setTabPaneAttributes(element, active, id);
8839
9543
  element.classList.add("tab-pane");
8840
- if (active) {
8841
- element.classList.add("active");
8842
- }
9544
+ element.classList.toggle("active", active);
8843
9545
  }
8844
9546
  infoAsModal(info, id, config = {}) {
8845
9547
  const modal = document.createElement("div");