@structured-field/widget-editor 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- var script$c = {
1
+ var script$d = {
2
2
  name: 'StringEditor',
3
3
  props: {
4
4
  schema: { type: Object, required: true },
@@ -1583,6 +1583,44 @@ const toReadonly = (value) => isObject(value) ? /* @__PURE__ */ readonly(value)
1583
1583
  function isRef(r) {
1584
1584
  return r ? r["__v_isRef"] === true : false;
1585
1585
  }
1586
+ // @__NO_SIDE_EFFECTS__
1587
+ function ref(value) {
1588
+ return createRef(value, false);
1589
+ }
1590
+ function createRef(rawValue, shallow) {
1591
+ if (/* @__PURE__ */ isRef(rawValue)) {
1592
+ return rawValue;
1593
+ }
1594
+ return new RefImpl(rawValue, shallow);
1595
+ }
1596
+ class RefImpl {
1597
+ constructor(value, isShallow2) {
1598
+ this.dep = new Dep();
1599
+ this["__v_isRef"] = true;
1600
+ this["__v_isShallow"] = false;
1601
+ this._rawValue = isShallow2 ? value : toRaw(value);
1602
+ this._value = isShallow2 ? value : toReactive(value);
1603
+ this["__v_isShallow"] = isShallow2;
1604
+ }
1605
+ get value() {
1606
+ {
1607
+ this.dep.track();
1608
+ }
1609
+ return this._value;
1610
+ }
1611
+ set value(newValue) {
1612
+ const oldValue = this._rawValue;
1613
+ const useDirectValue = this["__v_isShallow"] || isShallow(newValue) || isReadonly(newValue);
1614
+ newValue = useDirectValue ? newValue : toRaw(newValue);
1615
+ if (hasChanged(newValue, oldValue)) {
1616
+ this._rawValue = newValue;
1617
+ this._value = useDirectValue ? newValue : toReactive(newValue);
1618
+ {
1619
+ this.dep.trigger();
1620
+ }
1621
+ }
1622
+ }
1623
+ }
1586
1624
  function unref(ref2) {
1587
1625
  return /* @__PURE__ */ isRef(ref2) ? ref2.value : ref2;
1588
1626
  }
@@ -5974,6 +6012,32 @@ const computed = (getterOrOptions, debugOptions) => {
5974
6012
  return c;
5975
6013
  };
5976
6014
 
6015
+ function h(type, propsOrChildren, children) {
6016
+ try {
6017
+ setBlockTracking(-1);
6018
+ const l = arguments.length;
6019
+ if (l === 2) {
6020
+ if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
6021
+ if (isVNode(propsOrChildren)) {
6022
+ return createVNode(type, null, [propsOrChildren]);
6023
+ }
6024
+ return createVNode(type, propsOrChildren);
6025
+ } else {
6026
+ return createVNode(type, null, propsOrChildren);
6027
+ }
6028
+ } else {
6029
+ if (l > 3) {
6030
+ children = Array.prototype.slice.call(arguments, 2);
6031
+ } else if (l === 3 && isVNode(children)) {
6032
+ children = [children];
6033
+ }
6034
+ return createVNode(type, propsOrChildren, children);
6035
+ }
6036
+ } finally {
6037
+ setBlockTracking(1);
6038
+ }
6039
+ }
6040
+
5977
6041
  const version = "3.5.30";
5978
6042
 
5979
6043
  /**
@@ -7044,10 +7108,10 @@ function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7044
7108
  ], 2 /* CLASS */))
7045
7109
  }
7046
7110
 
7047
- script$c.render = render$c;
7048
- script$c.__file = "src/editors/StringEditor.vue";
7111
+ script$d.render = render$c;
7112
+ script$d.__file = "src/editors/StringEditor.vue";
7049
7113
 
7050
- var script$b = {
7114
+ var script$c = {
7051
7115
  name: 'NumberEditor',
7052
7116
  props: {
7053
7117
  schema: { type: Object, required: true },
@@ -7120,10 +7184,10 @@ function render$b(_ctx, _cache, $props, $setup, $data, $options) {
7120
7184
  ], 2 /* CLASS */))
7121
7185
  }
7122
7186
 
7123
- script$b.render = render$b;
7124
- script$b.__file = "src/editors/NumberEditor.vue";
7187
+ script$c.render = render$b;
7188
+ script$c.__file = "src/editors/NumberEditor.vue";
7125
7189
 
7126
- var script$a = {
7190
+ var script$b = {
7127
7191
  name: 'BooleanEditor',
7128
7192
  props: {
7129
7193
  schema: { type: Object, required: true },
@@ -7179,10 +7243,10 @@ function render$a(_ctx, _cache, $props, $setup, $data, $options) {
7179
7243
  ], 2 /* CLASS */))
7180
7244
  }
7181
7245
 
7182
- script$a.render = render$a;
7183
- script$a.__file = "src/editors/BooleanEditor.vue";
7246
+ script$b.render = render$a;
7247
+ script$b.__file = "src/editors/BooleanEditor.vue";
7184
7248
 
7185
- var script$9 = {
7249
+ var script$a = {
7186
7250
  name: 'SelectEditor',
7187
7251
  props: {
7188
7252
  schema: { type: Object, required: true },
@@ -7251,10 +7315,10 @@ function render$9(_ctx, _cache, $props, $setup, $data, $options) {
7251
7315
  ], 2 /* CLASS */))
7252
7316
  }
7253
7317
 
7254
- script$9.render = render$9;
7255
- script$9.__file = "src/editors/SelectEditor.vue";
7318
+ script$a.render = render$9;
7319
+ script$a.__file = "src/editors/SelectEditor.vue";
7256
7320
 
7257
- var script$8 = {
7321
+ var script$9 = {
7258
7322
  name: 'HiddenEditor',
7259
7323
  props: {
7260
7324
  schema: { type: Object, required: true },
@@ -7288,10 +7352,10 @@ function render$8(_ctx, _cache, $props, $setup, $data, $options) {
7288
7352
  return (openBlock(), createElementBlock("div", _hoisted_1$7))
7289
7353
  }
7290
7354
 
7291
- script$8.render = render$8;
7292
- script$8.__file = "src/editors/HiddenEditor.vue";
7355
+ script$9.render = render$8;
7356
+ script$9.__file = "src/editors/HiddenEditor.vue";
7293
7357
 
7294
- var script$7 = {
7358
+ var script$8 = {
7295
7359
  name: 'SfIcon',
7296
7360
  props: {
7297
7361
  name: { type: String, required: true },
@@ -7383,15 +7447,264 @@ function render$7(_ctx, _cache, $props, $setup, $data, $options) {
7383
7447
  ], 8 /* PROPS */, _hoisted_1$6))
7384
7448
  }
7385
7449
 
7386
- script$7.render = render$7;
7387
- script$7.__file = "src/editors/SfIcon.vue";
7450
+ script$8.render = render$7;
7451
+ script$8.__file = "src/editors/SfIcon.vue";
7452
+
7453
+ // JSON Schema conditional evaluation for form rendering.
7454
+ //
7455
+ // Supports the standard keywords: `if`/`then`/`else`, `allOf` of those,
7456
+ // `dependentSchemas`, and `dependentRequired`. The matcher implements the
7457
+ // subset of JSON Schema validation that is meaningful for form-time
7458
+ // conditionals on object properties:
7459
+ //
7460
+ // - `properties: { field: { const, enum, type, not } }`
7461
+ // - `required: [...]` (treated as "key is present and not null/undefined")
7462
+ // - `not`, `allOf`, `anyOf`, `oneOf` (recursive)
7463
+ //
7464
+ // The functions are pure: they take a schema + value and return an
7465
+ // "effective schema" with `properties`/`required` merged from any matching
7466
+ // branches. The renderer uses that effective schema instead of the raw one.
7467
+
7468
+ function isPresent(value, key) {
7469
+ if (value == null || typeof value !== 'object') return false;
7470
+ if (!(key in value)) return false;
7471
+ const v = value[key];
7472
+ return v !== undefined && v !== null && v !== '';
7473
+ }
7474
+
7475
+ function matchesPropertyConstraint(value, constraint) {
7476
+ if (!constraint || typeof constraint !== 'object') return true;
7477
+ if ('const' in constraint) return value === constraint.const;
7478
+ if (Array.isArray(constraint.enum)) return constraint.enum.includes(value);
7479
+ if (constraint.type) {
7480
+ const t = constraint.type;
7481
+ if (t === 'string' && typeof value !== 'string') return false;
7482
+ if (t === 'number' && typeof value !== 'number') return false;
7483
+ if (t === 'integer' && (typeof value !== 'number' || !Number.isInteger(value))) return false;
7484
+ if (t === 'boolean' && typeof value !== 'boolean') return false;
7485
+ if (t === 'null' && value !== null) return false;
7486
+ if (t === 'array' && !Array.isArray(value)) return false;
7487
+ if (t === 'object' && (value == null || typeof value !== 'object' || Array.isArray(value))) return false;
7488
+ }
7489
+ // Numeric comparators
7490
+ if (typeof value === 'number') {
7491
+ if (typeof constraint.minimum === 'number' && value < constraint.minimum) return false;
7492
+ if (typeof constraint.maximum === 'number' && value > constraint.maximum) return false;
7493
+ if (typeof constraint.exclusiveMinimum === 'number' && value <= constraint.exclusiveMinimum) return false;
7494
+ if (typeof constraint.exclusiveMaximum === 'number' && value >= constraint.exclusiveMaximum) return false;
7495
+ if (typeof constraint.multipleOf === 'number' && constraint.multipleOf > 0) {
7496
+ const q = value / constraint.multipleOf;
7497
+ if (Math.abs(q - Math.round(q)) > 1e-9) return false;
7498
+ }
7499
+ }
7500
+ // String comparators
7501
+ if (typeof value === 'string') {
7502
+ if (typeof constraint.minLength === 'number' && value.length < constraint.minLength) return false;
7503
+ if (typeof constraint.maxLength === 'number' && value.length > constraint.maxLength) return false;
7504
+ if (typeof constraint.pattern === 'string') {
7505
+ try {
7506
+ if (!new RegExp(constraint.pattern).test(value)) return false;
7507
+ } catch (e) {
7508
+ // Invalid pattern — treat as non-match rather than throwing in render path.
7509
+ return false;
7510
+ }
7511
+ }
7512
+ }
7513
+ if (constraint.not) return !matchesSchema(value, constraint.not);
7514
+ return true;
7515
+ }
7516
+
7517
+ // Returns true if `value` (an object) satisfies the form-relevant subset of `schema`.
7518
+ function matchesSchema(value, schema) {
7519
+ if (!schema || typeof schema !== 'object') return true;
7388
7520
 
7389
- var script$6 = {
7521
+ if (Array.isArray(schema.required)) {
7522
+ for (const k of schema.required) {
7523
+ if (!isPresent(value, k)) return false;
7524
+ }
7525
+ }
7526
+
7527
+ if (schema.properties && typeof schema.properties === 'object') {
7528
+ for (const [k, constraint] of Object.entries(schema.properties)) {
7529
+ // Standard JSON Schema: property constraints only apply if the key is present.
7530
+ if (value == null || !(k in value)) continue;
7531
+ if (!matchesPropertyConstraint(value[k], constraint)) return false;
7532
+ }
7533
+ }
7534
+
7535
+ if (schema.not && matchesSchema(value, schema.not)) return false;
7536
+ if (Array.isArray(schema.allOf) && !schema.allOf.every((s) => matchesSchema(value, s))) return false;
7537
+ if (Array.isArray(schema.anyOf) && !schema.anyOf.some((s) => matchesSchema(value, s))) return false;
7538
+ if (Array.isArray(schema.oneOf)) {
7539
+ const matched = schema.oneOf.filter((s) => matchesSchema(value, s)).length;
7540
+ if (matched !== 1) return false;
7541
+ }
7542
+
7543
+ return true;
7544
+ }
7545
+
7546
+ function insertAfter(properties, anchorKey, newEntries) {
7547
+ // Rebuild the property map so newly-added keys appear immediately after
7548
+ // their controlling field instead of being appended to the end.
7549
+ const existingKeys = Object.keys(properties);
7550
+ const anchorIdx = anchorKey ? existingKeys.indexOf(anchorKey) : -1;
7551
+ if (anchorIdx === -1) {
7552
+ const out = { ...properties };
7553
+ for (const [k, v] of newEntries) out[k] = v;
7554
+ return out;
7555
+ }
7556
+ const out = {};
7557
+ for (let i = 0; i < existingKeys.length; i++) {
7558
+ const key = existingKeys[i];
7559
+ out[key] = properties[key];
7560
+ if (i === anchorIdx) {
7561
+ for (const [k, v] of newEntries) {
7562
+ if (!(k in properties)) out[k] = v;
7563
+ }
7564
+ }
7565
+ }
7566
+ // Any new keys that already existed in properties have been kept in place;
7567
+ // overwrite their values with the merged versions.
7568
+ for (const [k, v] of newEntries) {
7569
+ if (k in properties) out[k] = v;
7570
+ }
7571
+ return out;
7572
+ }
7573
+
7574
+ function mergeBranch(target, branch, anchorKey) {
7575
+ if (!branch || typeof branch !== 'object') return target;
7576
+
7577
+ if (branch.properties) {
7578
+ const merged = [];
7579
+ for (const [k, v] of Object.entries(branch.properties)) {
7580
+ const existing = target.properties && target.properties[k];
7581
+ merged.push([k, existing ? { ...existing, ...v } : v]);
7582
+ }
7583
+ target.properties = insertAfter(target.properties || {}, anchorKey, merged);
7584
+ }
7585
+
7586
+ if (Array.isArray(branch.required)) {
7587
+ const set = new Set(target.required || []);
7588
+ for (const k of branch.required) set.add(k);
7589
+ target.required = Array.from(set);
7590
+ }
7591
+
7592
+ // Conditionals can also nest more conditionals — flatten them.
7593
+ if (branch.allOf) {
7594
+ target.allOf = [...(target.allOf || []), ...branch.allOf];
7595
+ }
7596
+ if (branch.if) {
7597
+ target.allOf = [...(target.allOf || []), { if: branch.if, then: branch.then, else: branch.else }];
7598
+ }
7599
+ if (branch.dependentSchemas) {
7600
+ target.dependentSchemas = { ...(target.dependentSchemas || {}), ...branch.dependentSchemas };
7601
+ }
7602
+ if (branch.dependentRequired) {
7603
+ target.dependentRequired = { ...(target.dependentRequired || {}), ...branch.dependentRequired };
7604
+ }
7605
+
7606
+ return target;
7607
+ }
7608
+
7609
+ // Returns an effective schema for an object schema given the current value.
7610
+ // Resolves `if/then/else`, `allOf` of those, `dependentSchemas`, and
7611
+ // `dependentRequired`. Idempotent and safe to call on every render.
7612
+ //
7613
+ // `resolver` (optional) is called on each `then`/`else`/`dependentSchemas`
7614
+ // branch before merging, so `$ref` inside conditional branches is followed.
7615
+ // Pass `form.resolveSchema` from the renderer.
7616
+ function applyConditionals(schema, value, resolver) {
7617
+ const resolve = typeof resolver === 'function' ? resolver : (s) => s;
7618
+ if (!schema || typeof schema !== 'object') return schema;
7619
+ if (schema.type !== 'object' && !schema.properties) return schema;
7620
+
7621
+ // Start with a shallow clone of the parts we may mutate.
7622
+ let effective = {
7623
+ ...schema,
7624
+ properties: { ...(schema.properties || {}) },
7625
+ required: Array.isArray(schema.required) ? [...schema.required] : [],
7626
+ };
7627
+
7628
+ const safeValue = value && typeof value === 'object' ? value : {};
7629
+
7630
+ // Pick the controlling field of an `if` clause so newly-added properties
7631
+ // can be inserted right after it in render order.
7632
+ const anchorOf = (ifClause) => {
7633
+ if (!ifClause || typeof ifClause !== 'object') return null;
7634
+ const props = ifClause.properties && Object.keys(ifClause.properties);
7635
+ if (props && props.length) return props[0];
7636
+ if (Array.isArray(ifClause.required) && ifClause.required.length) return ifClause.required[0];
7637
+ return null;
7638
+ };
7639
+
7640
+ // Collect rules: top-level if/then/else + every entry in allOf that has one.
7641
+ const rules = [];
7642
+ if (effective.if) {
7643
+ rules.push({ if: effective.if, then: effective.then, else: effective.else, anchor: anchorOf(effective.if) });
7644
+ }
7645
+ if (Array.isArray(effective.allOf)) {
7646
+ for (const entry of effective.allOf) {
7647
+ if (entry && typeof entry === 'object' && entry.if) {
7648
+ rules.push({ if: entry.if, then: entry.then, else: entry.else, anchor: anchorOf(entry.if) });
7649
+ } else if (entry && typeof entry === 'object' && (entry.properties || entry.required)) {
7650
+ // Plain allOf branch (e.g. shared base) — always merge.
7651
+ mergeBranch(effective, entry);
7652
+ }
7653
+ }
7654
+ }
7655
+
7656
+ // Iterate to a fixed point so newly-merged rules can themselves trigger further rules.
7657
+ // Capped to avoid pathological loops.
7658
+ for (let i = 0; i < 8; i++) {
7659
+ let changed = false;
7660
+ const before = JSON.stringify({ p: effective.properties, r: effective.required });
7661
+
7662
+ for (const rule of rules) {
7663
+ const matched = matchesSchema(safeValue, rule.if);
7664
+ const branch = matched ? rule.then : rule.else;
7665
+ if (branch) mergeBranch(effective, resolve(branch), rule.anchor);
7666
+ }
7667
+
7668
+ if (effective.dependentSchemas) {
7669
+ for (const [key, branch] of Object.entries(effective.dependentSchemas)) {
7670
+ if (isPresent(safeValue, key)) mergeBranch(effective, resolve(branch), key);
7671
+ }
7672
+ }
7673
+
7674
+ if (effective.dependentRequired) {
7675
+ for (const [key, requiredKeys] of Object.entries(effective.dependentRequired)) {
7676
+ if (isPresent(safeValue, key) && Array.isArray(requiredKeys)) {
7677
+ const set = new Set(effective.required || []);
7678
+ for (const k of requiredKeys) set.add(k);
7679
+ effective.required = Array.from(set);
7680
+ }
7681
+ }
7682
+ }
7683
+
7684
+ const after = JSON.stringify({ p: effective.properties, r: effective.required });
7685
+ if (after !== before) changed = true;
7686
+ if (!changed) break;
7687
+ }
7688
+
7689
+ return effective;
7690
+ }
7691
+
7692
+ // Returns true if the schema declares any form-relevant conditional logic.
7693
+ function hasConditionals(schema) {
7694
+ if (!schema || typeof schema !== 'object') return false;
7695
+ if (schema.if || schema.dependentSchemas || schema.dependentRequired) return true;
7696
+ if (Array.isArray(schema.allOf)) {
7697
+ return schema.allOf.some((e) => e && typeof e === 'object' && (e.if || e.dependentSchemas));
7698
+ }
7699
+ return false;
7700
+ }
7701
+
7702
+ var script$7 = {
7390
7703
  name: 'ObjectEditor',
7391
7704
  beforeCreate() {
7392
7705
  if (!this.$options.components) this.$options.components = {};
7393
7706
  this.$options.components.SchemaEditor = script$1;
7394
- this.$options.components.SfIcon = script$7;
7707
+ this.$options.components.SfIcon = script$8;
7395
7708
  },
7396
7709
  props: {
7397
7710
  schema: { type: Object, required: true },
@@ -7412,10 +7725,14 @@ var script$6 = {
7412
7725
  title() {
7413
7726
  return this.schema.title || this.humanize(this.path[this.path.length - 1]) || '';
7414
7727
  },
7728
+ effectiveSchema() {
7729
+ if (!hasConditionals(this.schema)) return this.schema;
7730
+ return applyConditionals(this.schema, this.modelValue || {}, this.form?.resolveSchema);
7731
+ },
7415
7732
  summary() {
7416
7733
  const val = this.modelValue || {};
7417
7734
  const parts = [];
7418
- for (const key of Object.keys(this.schema.properties || {})) {
7735
+ for (const key of Object.keys(this.effectiveSchema.properties || {})) {
7419
7736
  if (parts.length >= 3) break;
7420
7737
  const v = val[key];
7421
7738
  if (v !== null && v !== undefined && v !== '' && typeof v !== 'object') {
@@ -7438,7 +7755,22 @@ var script$6 = {
7438
7755
  },
7439
7756
  onChildChange(key, value) {
7440
7757
  const newVal = { ...(this.modelValue || {}), [key]: value };
7441
- this.$emit('update:modelValue', newVal);
7758
+ this.$emit('update:modelValue', this.pruneInactive(newVal));
7759
+ },
7760
+ pruneInactive(value) {
7761
+ if (!hasConditionals(this.schema)) return value;
7762
+ const effective = applyConditionals(this.schema, value, this.form?.resolveSchema);
7763
+ const allowed = new Set(Object.keys(effective.properties || {}));
7764
+ let changed = false;
7765
+ const out = {};
7766
+ for (const k of Object.keys(value)) {
7767
+ if (allowed.has(k)) {
7768
+ out[k] = value[k];
7769
+ } else {
7770
+ changed = true;
7771
+ }
7772
+ }
7773
+ return changed ? out : value;
7442
7774
  },
7443
7775
  },
7444
7776
  };
@@ -7464,7 +7796,7 @@ function render$6(_ctx, _cache, $props, $setup, $data, $options) {
7464
7796
  return ($options.isRoot)
7465
7797
  ? (openBlock(), createElementBlock("div", _hoisted_1$5, [
7466
7798
  createBaseVNode("div", _hoisted_2$4, [
7467
- (openBlock(true), createElementBlock(Fragment, null, renderList(($props.schema.properties || {}), (propSchema, key) => {
7799
+ (openBlock(true), createElementBlock(Fragment, null, renderList(($options.effectiveSchema.properties || {}), (propSchema, key) => {
7468
7800
  return (openBlock(), createBlock(_component_SchemaEditor, {
7469
7801
  key: key,
7470
7802
  schema: $props.form.resolveSchema(propSchema),
@@ -7498,7 +7830,7 @@ function render$6(_ctx, _cache, $props, $setup, $data, $options) {
7498
7830
  : createCommentVNode("v-if", true)
7499
7831
  ]),
7500
7832
  withDirectives(createBaseVNode("div", _hoisted_7$2, [
7501
- (openBlock(true), createElementBlock(Fragment, null, renderList(($props.schema.properties || {}), (propSchema, key) => {
7833
+ (openBlock(true), createElementBlock(Fragment, null, renderList(($options.effectiveSchema.properties || {}), (propSchema, key) => {
7502
7834
  return (openBlock(), createBlock(_component_SchemaEditor, {
7503
7835
  key: key,
7504
7836
  schema: $props.form.resolveSchema(propSchema),
@@ -7514,8 +7846,8 @@ function render$6(_ctx, _cache, $props, $setup, $data, $options) {
7514
7846
  ], 2 /* CLASS */))
7515
7847
  }
7516
7848
 
7517
- script$6.render = render$6;
7518
- script$6.__file = "src/editors/ObjectEditor.vue";
7849
+ script$7.render = render$6;
7850
+ script$7.__file = "src/editors/ObjectEditor.vue";
7519
7851
 
7520
7852
  function debounce(fn, delay) {
7521
7853
  let timer;
@@ -7558,12 +7890,12 @@ function getDefaultForSchema(schema) {
7558
7890
 
7559
7891
  let keyCounter = 0;
7560
7892
 
7561
- var script$5 = {
7893
+ var script$6 = {
7562
7894
  name: 'ArrayEditor',
7563
7895
  beforeCreate() {
7564
7896
  if (!this.$options.components) this.$options.components = {};
7565
7897
  this.$options.components.SchemaEditor = script$1;
7566
- this.$options.components.SfIcon = script$7;
7898
+ this.$options.components.SfIcon = script$8;
7567
7899
  },
7568
7900
  props: {
7569
7901
  schema: { type: Object, required: true },
@@ -7794,15 +8126,15 @@ function render$5(_ctx, _cache, $props, $setup, $data, $options) {
7794
8126
  ], 2 /* CLASS */))
7795
8127
  }
7796
8128
 
7797
- script$5.render = render$5;
7798
- script$5.__file = "src/editors/ArrayEditor.vue";
8129
+ script$6.render = render$5;
8130
+ script$6.__file = "src/editors/ArrayEditor.vue";
7799
8131
 
7800
- var script$4 = {
8132
+ var script$5 = {
7801
8133
  name: 'NullableEditor',
7802
8134
  beforeCreate() {
7803
8135
  if (!this.$options.components) this.$options.components = {};
7804
8136
  this.$options.components.SchemaEditor = script$1;
7805
- this.$options.components.SfIcon = script$7;
8137
+ this.$options.components.SfIcon = script$8;
7806
8138
  },
7807
8139
  props: {
7808
8140
  schema: { type: Object, required: true },
@@ -7918,10 +8250,10 @@ function render$4(_ctx, _cache, $props, $setup, $data, $options) {
7918
8250
  ], 2 /* CLASS */))
7919
8251
  }
7920
8252
 
7921
- script$4.render = render$4;
7922
- script$4.__file = "src/editors/NullableEditor.vue";
8253
+ script$5.render = render$4;
8254
+ script$5.__file = "src/editors/NullableEditor.vue";
7923
8255
 
7924
- var script$3 = {
8256
+ var script$4 = {
7925
8257
  name: 'UnionEditor',
7926
8258
  beforeCreate() {
7927
8259
  if (!this.$options.components) this.$options.components = {};
@@ -8021,12 +8353,12 @@ function render$3(_ctx, _cache, $props, $setup, $data, $options) {
8021
8353
  ]))
8022
8354
  }
8023
8355
 
8024
- script$3.render = render$3;
8025
- script$3.__file = "src/editors/UnionEditor.vue";
8356
+ script$4.render = render$3;
8357
+ script$4.__file = "src/editors/UnionEditor.vue";
8026
8358
 
8027
- var script$2 = {
8359
+ var script$3 = {
8028
8360
  name: 'RelationEditor',
8029
- components: { SfIcon: script$7 },
8361
+ components: { SfIcon: script$8 },
8030
8362
  props: {
8031
8363
  schema: { type: Object, required: true },
8032
8364
  modelValue: { default: null },
@@ -8294,24 +8626,82 @@ function render$2(_ctx, _cache, $props, $setup, $data, $options) {
8294
8626
  ], 2 /* CLASS */))
8295
8627
  }
8296
8628
 
8297
- script$2.render = render$2;
8298
- script$2.__file = "src/editors/RelationEditor.vue";
8629
+ script$3.render = render$2;
8630
+ script$3.__file = "src/editors/RelationEditor.vue";
8631
+
8632
+ var script$2 = {
8633
+ name: 'WebComponentWrapper',
8634
+ props: {
8635
+ tagName: { type: String, required: true },
8636
+ schema: { type: Object, required: true },
8637
+ modelValue: { default: undefined },
8638
+ path: { type: Array, default: () => [] },
8639
+ form: { type: Object, required: true },
8640
+ },
8641
+ emits: ['update:modelValue'],
8642
+ setup(props, { emit }) {
8643
+ const elRef = ref(null);
8644
+
8645
+ function syncProps() {
8646
+ const el = elRef.value;
8647
+ if (!el) return;
8648
+ el.schema = props.schema;
8649
+ el.modelValue = props.modelValue;
8650
+ el.path = props.path;
8651
+ el.form = props.form;
8652
+ }
8653
+
8654
+ function handleChange(e) {
8655
+ const value = e.detail != null
8656
+ ? (Array.isArray(e.detail) ? e.detail[0] : e.detail)
8657
+ : undefined;
8658
+ emit('update:modelValue', value);
8659
+ }
8660
+
8661
+ onMounted(() => {
8662
+ syncProps();
8663
+ const el = elRef.value;
8664
+ if (el) {
8665
+ el.addEventListener('update:model-value', handleChange);
8666
+ el.addEventListener('change', handleChange);
8667
+ }
8668
+ });
8669
+
8670
+ onBeforeUnmount(() => {
8671
+ const el = elRef.value;
8672
+ if (el) {
8673
+ el.removeEventListener('update:model-value', handleChange);
8674
+ el.removeEventListener('change', handleChange);
8675
+ }
8676
+ });
8677
+
8678
+ watch(() => [props.schema, props.modelValue, props.path, props.form], syncProps, { deep: true });
8679
+
8680
+ return () => h(props.tagName, { ref: elRef });
8681
+ },
8682
+ };
8683
+
8684
+ script$2.__file = "src/editors/WebComponentWrapper.vue";
8299
8685
 
8300
8686
  const MAX_DEPTH = 12;
8301
8687
 
8302
8688
  var script$1 = {
8303
8689
  name: 'SchemaEditor',
8304
8690
  components: {
8305
- StringEditor: script$c,
8306
- NumberEditor: script$b,
8307
- BooleanEditor: script$a,
8308
- SelectEditor: script$9,
8309
- HiddenEditor: script$8,
8310
- ObjectEditor: script$6,
8311
- ArrayEditor: script$5,
8312
- NullableEditor: script$4,
8313
- UnionEditor: script$3,
8314
- RelationEditor: script$2,
8691
+ StringEditor: script$d,
8692
+ NumberEditor: script$c,
8693
+ BooleanEditor: script$b,
8694
+ SelectEditor: script$a,
8695
+ HiddenEditor: script$9,
8696
+ ObjectEditor: script$7,
8697
+ ArrayEditor: script$6,
8698
+ NullableEditor: script$5,
8699
+ UnionEditor: script$4,
8700
+ RelationEditor: script$3,
8701
+ WebComponentWrapper: script$2,
8702
+ },
8703
+ inject: {
8704
+ customEditors: { default: () => () => [] },
8315
8705
  },
8316
8706
  props: {
8317
8707
  schema: { type: Object, required: true },
@@ -8333,11 +8723,20 @@ var script$1 = {
8333
8723
  },
8334
8724
  },
8335
8725
  computed: {
8726
+ isWebComponent() {
8727
+ const c = this.editorComponent;
8728
+ return typeof c === 'string' && c.includes('-');
8729
+ },
8336
8730
  editorComponent() {
8337
8731
  const schema = this.schema;
8338
8732
 
8339
8733
  if (this.path.length > MAX_DEPTH) return 'StringEditor';
8340
8734
 
8735
+ const overrides = this.customEditors();
8736
+ for (const override of overrides) {
8737
+ if (override.match(schema, this.path)) return override.component;
8738
+ }
8739
+
8341
8740
  if (schema.type === 'relation') return 'RelationEditor';
8342
8741
  if (schema.oneOf && schema.discriminator) return 'UnionEditor';
8343
8742
  if ('const' in schema) return 'HiddenEditor';
@@ -8355,14 +8754,28 @@ var script$1 = {
8355
8754
  };
8356
8755
 
8357
8756
  function render$1(_ctx, _cache, $props, $setup, $data, $options) {
8358
- return (openBlock(), createBlock(resolveDynamicComponent($options.editorComponent), {
8359
- ref: "editor",
8360
- schema: $props.schema,
8361
- "model-value": $props.modelValue,
8362
- path: $props.path,
8363
- form: $props.form,
8364
- "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event)))
8365
- }, null, 8 /* PROPS */, ["schema", "model-value", "path", "form"]))
8757
+ const _component_WebComponentWrapper = resolveComponent("WebComponentWrapper");
8758
+
8759
+ return ($options.isWebComponent)
8760
+ ? (openBlock(), createBlock(_component_WebComponentWrapper, {
8761
+ key: 0,
8762
+ ref: "editor",
8763
+ "tag-name": $options.editorComponent,
8764
+ schema: $props.schema,
8765
+ "model-value": $props.modelValue,
8766
+ path: $props.path,
8767
+ form: $props.form,
8768
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event)))
8769
+ }, null, 8 /* PROPS */, ["tag-name", "schema", "model-value", "path", "form"]))
8770
+ : (openBlock(), createBlock(resolveDynamicComponent($options.editorComponent), {
8771
+ key: 1,
8772
+ ref: "editor",
8773
+ schema: $props.schema,
8774
+ "model-value": $props.modelValue,
8775
+ path: $props.path,
8776
+ form: $props.form,
8777
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => (_ctx.$emit('update:modelValue', $event)))
8778
+ }, null, 8 /* PROPS */, ["schema", "model-value", "path", "form"]))
8366
8779
  }
8367
8780
 
8368
8781
  script$1.render = render$1;
@@ -8375,9 +8788,15 @@ var script = {
8375
8788
  schema: { type: [Object, String], default: () => ({}) },
8376
8789
  initialData: { default: undefined },
8377
8790
  errors: { type: Object, default: () => ({}) },
8791
+ customEditors: { type: Array, default: () => [] },
8378
8792
  },
8379
8793
  emits: ['change'],
8380
8794
  expose: ['getValue'],
8795
+ provide() {
8796
+ return {
8797
+ customEditors: () => this.customEditors,
8798
+ };
8799
+ },
8381
8800
  data() {
8382
8801
  const parsedSchema = typeof this.schema === 'string' ? JSON.parse(this.schema) : this.schema;
8383
8802
  const defs = parsedSchema.$defs || parsedSchema.definitions || {};
@@ -8395,6 +8814,7 @@ var script = {
8395
8814
  return {
8396
8815
  resolveSchema: (s) => this.resolveSchema(s),
8397
8816
  getSchemaAtPath: (p) => this.getSchemaAtPath(p),
8817
+ getEffectiveSchemaAtPath: (p) => this.getEffectiveSchemaAtPath(p),
8398
8818
  getErrorsForPath: (p) => this.getErrorsForPath(p),
8399
8819
  };
8400
8820
  },
@@ -8477,6 +8897,28 @@ var script = {
8477
8897
  return schema;
8478
8898
  },
8479
8899
 
8900
+ getEffectiveSchemaAtPath(path) {
8901
+ let schema = this.resolveSchema(this.rootSchema);
8902
+ let value = this.currentValue;
8903
+ for (const segment of path) {
8904
+ if (!schema) return null;
8905
+ if (schema.properties || schema.if || schema.allOf || schema.dependentSchemas) {
8906
+ if (hasConditionals(schema)) schema = applyConditionals(schema, value || {}, (s) => this.resolveSchema(s));
8907
+ }
8908
+ if (schema.properties && schema.properties[segment] !== undefined) {
8909
+ schema = this.resolveSchema(schema.properties[segment]);
8910
+ value = value != null ? value[segment] : undefined;
8911
+ } else if (schema.items) {
8912
+ schema = this.resolveSchema(schema.items);
8913
+ value = Array.isArray(value) ? value[segment] : undefined;
8914
+ } else {
8915
+ return null;
8916
+ }
8917
+ }
8918
+ if (schema && hasConditionals(schema)) schema = applyConditionals(schema, value || {});
8919
+ return schema;
8920
+ },
8921
+
8480
8922
  onValueChange(val) {
8481
8923
  this.currentValue = val;
8482
8924
  this.$emit('change', val);
@@ -8516,6 +8958,115 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
8516
8958
  script.render = render;
8517
8959
  script.__file = "src/SchemaForm.vue";
8518
8960
 
8961
+ /**
8962
+ * Base class for creating web component custom editors.
8963
+ *
8964
+ * Handles the property contract with the structured-widget-editor wrapper:
8965
+ * - Receives `schema`, `modelValue`, `path`, and `form` as JS properties.
8966
+ * - Provides `emitChange(value)` to dispatch the value back to the form.
8967
+ * - Provides `getErrors()` to retrieve validation errors for this field.
8968
+ * - Calls `render()` once on `connectedCallback` and `update()` on property changes.
8969
+ *
8970
+ * @example
8971
+ * import { BaseEditorElement } from '@structured-field/widget-editor';
8972
+ *
8973
+ * class MyColorPicker extends BaseEditorElement {
8974
+ * render() {
8975
+ * const input = document.createElement('input');
8976
+ * input.type = 'color';
8977
+ * input.value = this.modelValue || '#000000';
8978
+ * input.addEventListener('input', () => this.emitChange(input.value));
8979
+ * this._input = input;
8980
+ * this.appendChild(input);
8981
+ * }
8982
+ *
8983
+ * update() {
8984
+ * if (this._input) this._input.value = this.modelValue || '#000000';
8985
+ * }
8986
+ * }
8987
+ *
8988
+ * customElements.define('my-color-picker', MyColorPicker);
8989
+ */
8990
+ class BaseEditorElement extends HTMLElement {
8991
+ constructor() {
8992
+ super();
8993
+ this._schema = {};
8994
+ this._modelValue = undefined;
8995
+ this._path = [];
8996
+ this._form = null;
8997
+ this._connected = false;
8998
+ }
8999
+
9000
+ /* ── Property contract ─────────────────────────────────────────────── */
9001
+
9002
+ get schema() { return this._schema; }
9003
+ set schema(v) {
9004
+ this._schema = v;
9005
+ if (this._connected) this.update();
9006
+ }
9007
+
9008
+ get modelValue() { return this._modelValue; }
9009
+ set modelValue(v) {
9010
+ this._modelValue = v;
9011
+ if (this._connected) this.update();
9012
+ }
9013
+
9014
+ get path() { return this._path; }
9015
+ set path(v) {
9016
+ this._path = v;
9017
+ if (this._connected) this.update();
9018
+ }
9019
+
9020
+ get form() { return this._form; }
9021
+ set form(v) {
9022
+ this._form = v;
9023
+ if (this._connected) this.update();
9024
+ }
9025
+
9026
+ /* ── Lifecycle ─────────────────────────────────────────────────────── */
9027
+
9028
+ connectedCallback() {
9029
+ this._connected = true;
9030
+ this.render();
9031
+ }
9032
+
9033
+ disconnectedCallback() {
9034
+ this._connected = false;
9035
+ }
9036
+
9037
+ /* ── Helpers ───────────────────────────────────────────────────────── */
9038
+
9039
+ /**
9040
+ * Dispatch the new value back to the form.
9041
+ * @param {*} value - The new field value.
9042
+ */
9043
+ emitChange(value) {
9044
+ this.dispatchEvent(new CustomEvent('change', { detail: value }));
9045
+ }
9046
+
9047
+ /**
9048
+ * Returns the current validation errors for this field.
9049
+ * @returns {string[]}
9050
+ */
9051
+ getErrors() {
9052
+ return this._form?.getErrorsForPath?.(this._path) ?? [];
9053
+ }
9054
+
9055
+ /* ── Override points ───────────────────────────────────────────────── */
9056
+
9057
+ /**
9058
+ * Called once when the element is connected to the DOM.
9059
+ * Build the initial DOM structure here.
9060
+ */
9061
+ render() {}
9062
+
9063
+ /**
9064
+ * Called whenever a property (schema, modelValue, path, form) changes
9065
+ * after the initial render. Update the DOM here.
9066
+ */
9067
+ update() {}
9068
+ }
9069
+
8519
9070
  const SchemaFormElement = defineCustomElement({
8520
9071
  ...script,
8521
9072
  shadowRoot: false,
@@ -8527,5 +9078,5 @@ function registerCustomElement(tagName = 'schema-form') {
8527
9078
  }
8528
9079
  }
8529
9080
 
8530
- export { script$5 as ArrayEditor, script$a as BooleanEditor, script$8 as HiddenEditor, script$4 as NullableEditor, script$b as NumberEditor, script$6 as ObjectEditor, script$2 as RelationEditor, script$1 as SchemaEditor, script as SchemaForm, SchemaFormElement, script$9 as SelectEditor, script$c as StringEditor, script$3 as UnionEditor, registerCustomElement };
9081
+ export { script$6 as ArrayEditor, BaseEditorElement, script$b as BooleanEditor, script$9 as HiddenEditor, script$5 as NullableEditor, script$c as NumberEditor, script$7 as ObjectEditor, script$3 as RelationEditor, script$1 as SchemaEditor, script as SchemaForm, SchemaFormElement, script$a as SelectEditor, script$d as StringEditor, script$4 as UnionEditor, script$2 as WebComponentWrapper, applyConditionals, hasConditionals, matchesSchema, registerCustomElement };
8531
9082
  //# sourceMappingURL=structured-widget-editor.esm.js.map