@structured-field/widget-editor 1.2.1 → 1.3.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$d = {
1
+ var script$f = {
2
2
  name: 'StringEditor',
3
3
  props: {
4
4
  schema: { type: Object, required: true },
@@ -21,6 +21,12 @@ var script$d = {
21
21
  isLong() {
22
22
  return this.schema.maxLength > 255 || this.schema.format === 'textarea';
23
23
  },
24
+ isNullable() {
25
+ return !!this.schema._nullable;
26
+ },
27
+ isNullValue() {
28
+ return this.modelValue === null || this.modelValue === undefined;
29
+ },
24
30
  fieldErrors() {
25
31
  if (!this.form || !this.form.getErrorsForPath) return [];
26
32
  return this.form.getErrorsForPath(this.path);
@@ -6759,7 +6765,7 @@ class VueElement extends BaseClass {
6759
6765
  _update() {
6760
6766
  const vnode = this._createVNode();
6761
6767
  if (this._app) vnode.appContext = this._app._context;
6762
- render$d(vnode, this._root);
6768
+ render$f(vnode, this._root);
6763
6769
  }
6764
6770
  _createVNode() {
6765
6771
  const baseProps = {};
@@ -7026,7 +7032,7 @@ let renderer;
7026
7032
  function ensureRenderer() {
7027
7033
  return renderer || (renderer = createRenderer(rendererOptions));
7028
7034
  }
7029
- const render$d = ((...args) => {
7035
+ const render$f = ((...args) => {
7030
7036
  ensureRenderer().render(...args);
7031
7037
  });
7032
7038
  const createApp = ((...args) => {
@@ -7067,39 +7073,61 @@ function normalizeContainer(container) {
7067
7073
  return container;
7068
7074
  }
7069
7075
 
7070
- const _hoisted_1$b = ["value", "placeholder"];
7071
- const _hoisted_2$9 = ["value", "placeholder"];
7072
- const _hoisted_3$8 = {
7073
- key: 2,
7076
+ const _hoisted_1$d = {
7077
+ key: 0,
7078
+ class: "sf-null-badge"
7079
+ };
7080
+ const _hoisted_2$b = ["value", "placeholder"];
7081
+ const _hoisted_3$b = ["value", "placeholder"];
7082
+ const _hoisted_4$7 = {
7083
+ key: 0,
7074
7084
  class: "errorlist"
7075
7085
  };
7076
7086
 
7077
- function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7087
+ function render$e(_ctx, _cache, $props, $setup, $data, $options) {
7078
7088
  return (openBlock(), createElementBlock("div", {
7079
7089
  class: normalizeClass(["sf-field", { errors: $options.fieldErrors.length }])
7080
7090
  }, [
7081
- createBaseVNode("label", {
7091
+ createBaseVNode("span", {
7082
7092
  class: normalizeClass(["sf-label", { required: $options.isRequired }])
7083
- }, toDisplayString($options.title), 3 /* TEXT, CLASS */),
7084
- ($options.isLong)
7085
- ? (openBlock(), createElementBlock("textarea", {
7086
- key: 0,
7087
- class: "sf-input sf-textarea",
7088
- rows: "3",
7089
- value: $props.modelValue,
7090
- placeholder: $props.schema.placeholder || '',
7091
- onInput: _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event.target.value)))
7092
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_1$b))
7093
- : (openBlock(), createElementBlock("input", {
7094
- key: 1,
7095
- type: "text",
7096
- class: "sf-input",
7097
- value: $props.modelValue != null ? String($props.modelValue) : '',
7098
- placeholder: $props.schema.placeholder || '',
7099
- onInput: _cache[1] || (_cache[1] = $event => (_ctx.$emit('update:modelValue', $event.target.value)))
7100
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$9)),
7093
+ }, [
7094
+ createTextVNode(toDisplayString($options.title) + " ", 1 /* TEXT */),
7095
+ ($options.isNullable && $options.isNullValue)
7096
+ ? (openBlock(), createElementBlock("span", _hoisted_1$d, "null"))
7097
+ : createCommentVNode("v-if", true)
7098
+ ], 2 /* CLASS */),
7099
+ createBaseVNode("div", {
7100
+ class: normalizeClass($options.isNullable ? 'sf-input-row' : null)
7101
+ }, [
7102
+ ($options.isLong)
7103
+ ? (openBlock(), createElementBlock("textarea", {
7104
+ key: 0,
7105
+ class: "sf-input sf-textarea",
7106
+ rows: "3",
7107
+ value: $options.isNullValue ? '' : $props.modelValue,
7108
+ placeholder: $options.isNullValue ? 'null' : ($props.schema.placeholder || ''),
7109
+ onInput: _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event.target.value)))
7110
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$b))
7111
+ : (openBlock(), createElementBlock("input", {
7112
+ key: 1,
7113
+ type: "text",
7114
+ class: "sf-input",
7115
+ value: $options.isNullValue ? '' : ($props.modelValue != null ? String($props.modelValue) : ''),
7116
+ placeholder: $options.isNullValue ? 'null' : ($props.schema.placeholder || ''),
7117
+ onInput: _cache[1] || (_cache[1] = $event => (_ctx.$emit('update:modelValue', $event.target.value)))
7118
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_3$b)),
7119
+ ($options.isNullable && !$options.isNullValue)
7120
+ ? (openBlock(), createElementBlock("button", {
7121
+ key: 2,
7122
+ type: "button",
7123
+ class: "sf-null-clear-btn",
7124
+ title: "Set to null",
7125
+ onClick: _cache[2] || (_cache[2] = $event => (_ctx.$emit('update:modelValue', null)))
7126
+ }, "✕"))
7127
+ : createCommentVNode("v-if", true)
7128
+ ], 2 /* CLASS */),
7101
7129
  ($options.fieldErrors.length)
7102
- ? (openBlock(), createElementBlock("ul", _hoisted_3$8, [
7130
+ ? (openBlock(), createElementBlock("ul", _hoisted_4$7, [
7103
7131
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.fieldErrors, (err, i) => {
7104
7132
  return (openBlock(), createElementBlock("li", { key: i }, toDisplayString(err), 1 /* TEXT */))
7105
7133
  }), 128 /* KEYED_FRAGMENT */))
@@ -7108,10 +7136,10 @@ function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7108
7136
  ], 2 /* CLASS */))
7109
7137
  }
7110
7138
 
7111
- script$d.render = render$c;
7112
- script$d.__file = "src/editors/StringEditor.vue";
7139
+ script$f.render = render$e;
7140
+ script$f.__file = "src/editors/StringEditor.vue";
7113
7141
 
7114
- var script$c = {
7142
+ var script$e = {
7115
7143
  name: 'NumberEditor',
7116
7144
  props: {
7117
7145
  schema: { type: Object, required: true },
@@ -7131,6 +7159,12 @@ var script$c = {
7131
7159
  const parentSchema = this.form.getSchemaAtPath(parentPath);
7132
7160
  return parentSchema && Array.isArray(parentSchema.required) && parentSchema.required.includes(fieldName);
7133
7161
  },
7162
+ isNullable() {
7163
+ return !!this.schema._nullable;
7164
+ },
7165
+ isNullValue() {
7166
+ return this.modelValue === null || this.modelValue === undefined;
7167
+ },
7134
7168
  fieldErrors() {
7135
7169
  if (!this.form || !this.form.getErrorsForPath) return [];
7136
7170
  return this.form.getErrorsForPath(this.path);
@@ -7152,30 +7186,53 @@ var script$c = {
7152
7186
  },
7153
7187
  };
7154
7188
 
7155
- const _hoisted_1$a = ["step", "min", "max", "value"];
7156
- const _hoisted_2$8 = {
7189
+ const _hoisted_1$c = {
7190
+ key: 0,
7191
+ class: "sf-null-badge"
7192
+ };
7193
+ const _hoisted_2$a = ["step", "min", "max", "value", "placeholder"];
7194
+ const _hoisted_3$a = {
7157
7195
  key: 0,
7158
7196
  class: "errorlist"
7159
7197
  };
7160
7198
 
7161
- function render$b(_ctx, _cache, $props, $setup, $data, $options) {
7199
+ function render$d(_ctx, _cache, $props, $setup, $data, $options) {
7162
7200
  return (openBlock(), createElementBlock("div", {
7163
7201
  class: normalizeClass(["sf-field", { errors: $options.fieldErrors.length }])
7164
7202
  }, [
7165
- createBaseVNode("label", {
7203
+ createBaseVNode("span", {
7166
7204
  class: normalizeClass(["sf-label", { required: $options.isRequired }])
7167
- }, toDisplayString($options.title), 3 /* TEXT, CLASS */),
7168
- createBaseVNode("input", {
7169
- type: "number",
7170
- class: "sf-input",
7171
- step: $props.schema.type === 'integer' ? '1' : 'any',
7172
- min: $props.schema.minimum != null ? String($props.schema.minimum) : undefined,
7173
- max: $props.schema.maximum != null ? String($props.schema.maximum) : undefined,
7174
- value: $props.modelValue != null ? $props.modelValue : '',
7175
- onInput: _cache[0] || (_cache[0] = (...args) => ($options.onInput && $options.onInput(...args)))
7176
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_1$a),
7205
+ }, [
7206
+ createTextVNode(toDisplayString($options.title) + " ", 1 /* TEXT */),
7207
+ ($options.isNullable && $options.isNullValue)
7208
+ ? (openBlock(), createElementBlock("span", _hoisted_1$c, "null"))
7209
+ : createCommentVNode("v-if", true)
7210
+ ], 2 /* CLASS */),
7211
+ createBaseVNode("div", {
7212
+ class: normalizeClass($options.isNullable ? 'sf-input-row' : null)
7213
+ }, [
7214
+ createBaseVNode("input", {
7215
+ type: "number",
7216
+ class: "sf-input",
7217
+ step: $props.schema.type === 'integer' ? '1' : 'any',
7218
+ min: $props.schema.minimum != null ? String($props.schema.minimum) : undefined,
7219
+ max: $props.schema.maximum != null ? String($props.schema.maximum) : undefined,
7220
+ value: $options.isNullValue ? '' : $props.modelValue,
7221
+ placeholder: $options.isNullValue ? 'null' : undefined,
7222
+ onInput: _cache[0] || (_cache[0] = (...args) => ($options.onInput && $options.onInput(...args)))
7223
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$a),
7224
+ ($options.isNullable && !$options.isNullValue)
7225
+ ? (openBlock(), createElementBlock("button", {
7226
+ key: 0,
7227
+ type: "button",
7228
+ class: "sf-null-clear-btn",
7229
+ title: "Set to null",
7230
+ onClick: _cache[1] || (_cache[1] = $event => (_ctx.$emit('update:modelValue', null)))
7231
+ }, "✕"))
7232
+ : createCommentVNode("v-if", true)
7233
+ ], 2 /* CLASS */),
7177
7234
  ($options.fieldErrors.length)
7178
- ? (openBlock(), createElementBlock("ul", _hoisted_2$8, [
7235
+ ? (openBlock(), createElementBlock("ul", _hoisted_3$a, [
7179
7236
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.fieldErrors, (err, i) => {
7180
7237
  return (openBlock(), createElementBlock("li", { key: i }, toDisplayString(err), 1 /* TEXT */))
7181
7238
  }), 128 /* KEYED_FRAGMENT */))
@@ -7184,10 +7241,10 @@ function render$b(_ctx, _cache, $props, $setup, $data, $options) {
7184
7241
  ], 2 /* CLASS */))
7185
7242
  }
7186
7243
 
7187
- script$c.render = render$b;
7188
- script$c.__file = "src/editors/NumberEditor.vue";
7244
+ script$e.render = render$d;
7245
+ script$e.__file = "src/editors/NumberEditor.vue";
7189
7246
 
7190
- var script$b = {
7247
+ var script$d = {
7191
7248
  name: 'BooleanEditor',
7192
7249
  props: {
7193
7250
  schema: { type: Object, required: true },
@@ -7200,6 +7257,12 @@ var script$b = {
7200
7257
  title() {
7201
7258
  return this.schema.title || this.humanize(this.path[this.path.length - 1]) || '';
7202
7259
  },
7260
+ isNullable() {
7261
+ return !!this.schema._nullable;
7262
+ },
7263
+ isNullValue() {
7264
+ return this.modelValue === null || this.modelValue === undefined;
7265
+ },
7203
7266
  fieldErrors() {
7204
7267
  if (!this.form || !this.form.getErrorsForPath) return [];
7205
7268
  return this.form.getErrorsForPath(this.path);
@@ -7213,28 +7276,47 @@ var script$b = {
7213
7276
  },
7214
7277
  };
7215
7278
 
7216
- const _hoisted_1$9 = { class: "sf-checkbox-label" };
7217
- const _hoisted_2$7 = ["checked"];
7218
- const _hoisted_3$7 = {
7279
+ const _hoisted_1$b = { class: "sf-boolean-row" };
7280
+ const _hoisted_2$9 = { class: "sf-checkbox-label" };
7281
+ const _hoisted_3$9 = ["checked"];
7282
+ const _hoisted_4$6 = {
7283
+ key: 0,
7284
+ class: "sf-null-badge"
7285
+ };
7286
+ const _hoisted_5$6 = {
7219
7287
  key: 0,
7220
7288
  class: "errorlist"
7221
7289
  };
7222
7290
 
7223
- function render$a(_ctx, _cache, $props, $setup, $data, $options) {
7291
+ function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7224
7292
  return (openBlock(), createElementBlock("div", {
7225
7293
  class: normalizeClass(["sf-field sf-field-boolean", { errors: $options.fieldErrors.length }])
7226
7294
  }, [
7227
- createBaseVNode("label", _hoisted_1$9, [
7228
- createBaseVNode("input", {
7229
- type: "checkbox",
7230
- class: "sf-checkbox",
7231
- checked: !!$props.modelValue,
7232
- onChange: _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event.target.checked)))
7233
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$7),
7234
- createTextVNode(" " + toDisplayString($options.title), 1 /* TEXT */)
7295
+ createBaseVNode("div", _hoisted_1$b, [
7296
+ createBaseVNode("label", _hoisted_2$9, [
7297
+ createBaseVNode("input", {
7298
+ type: "checkbox",
7299
+ class: "sf-checkbox",
7300
+ checked: !!$props.modelValue,
7301
+ onChange: _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event.target.checked)))
7302
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_3$9),
7303
+ createTextVNode(" " + toDisplayString($options.title) + " ", 1 /* TEXT */),
7304
+ ($options.isNullable && $options.isNullValue)
7305
+ ? (openBlock(), createElementBlock("span", _hoisted_4$6, "null"))
7306
+ : createCommentVNode("v-if", true)
7307
+ ]),
7308
+ ($options.isNullable && !$options.isNullValue)
7309
+ ? (openBlock(), createElementBlock("button", {
7310
+ key: 0,
7311
+ type: "button",
7312
+ class: "sf-null-clear-btn",
7313
+ title: "Set to null",
7314
+ onClick: _cache[1] || (_cache[1] = $event => (_ctx.$emit('update:modelValue', null)))
7315
+ }, "✕"))
7316
+ : createCommentVNode("v-if", true)
7235
7317
  ]),
7236
7318
  ($options.fieldErrors.length)
7237
- ? (openBlock(), createElementBlock("ul", _hoisted_3$7, [
7319
+ ? (openBlock(), createElementBlock("ul", _hoisted_5$6, [
7238
7320
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.fieldErrors, (err, i) => {
7239
7321
  return (openBlock(), createElementBlock("li", { key: i }, toDisplayString(err), 1 /* TEXT */))
7240
7322
  }), 128 /* KEYED_FRAGMENT */))
@@ -7243,10 +7325,194 @@ function render$a(_ctx, _cache, $props, $setup, $data, $options) {
7243
7325
  ], 2 /* CLASS */))
7244
7326
  }
7245
7327
 
7246
- script$b.render = render$a;
7247
- script$b.__file = "src/editors/BooleanEditor.vue";
7328
+ script$d.render = render$c;
7329
+ script$d.__file = "src/editors/BooleanEditor.vue";
7248
7330
 
7249
- var script$a = {
7331
+ var script$c = {
7332
+ name: 'DateEditor',
7333
+ props: {
7334
+ schema: { type: Object, required: true },
7335
+ modelValue: { default: '' },
7336
+ path: { type: Array, default: () => [] },
7337
+ form: { type: Object, default: null },
7338
+ },
7339
+ emits: ['update:modelValue'],
7340
+ computed: {
7341
+ isDateTime() {
7342
+ return this.schema.format === 'date-time';
7343
+ },
7344
+ inputType() {
7345
+ return this.isDateTime ? 'datetime-local' : 'date';
7346
+ },
7347
+ displayValue() {
7348
+ if (this.isNullValue) return '';
7349
+ const v = String(this.modelValue);
7350
+ if (this.isDateTime) {
7351
+ // Accept ISO 8601 strings like "2026-04-09T10:30:00[.sss][Z|+00:00]"
7352
+ // datetime-local expects "YYYY-MM-DDTHH:mm" (or with seconds).
7353
+ const match = v.match(/^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2})(?::(\d{2}(?:\.\d+)?))?/);
7354
+ if (match) {
7355
+ return match[3] ? `${match[1]}T${match[2]}:${match[3]}` : `${match[1]}T${match[2]}`;
7356
+ }
7357
+ return v;
7358
+ }
7359
+ // date: expect "YYYY-MM-DD"
7360
+ return v.slice(0, 10);
7361
+ },
7362
+ title() {
7363
+ return this.schema.title || this.humanize(this.path[this.path.length - 1]) || '';
7364
+ },
7365
+ isRequired() {
7366
+ if (this.path.length < 2 || !this.form) return false;
7367
+ const parentPath = this.path.slice(0, -1);
7368
+ const fieldName = this.path[this.path.length - 1];
7369
+ const parentSchema = this.form.getSchemaAtPath(parentPath);
7370
+ return parentSchema && Array.isArray(parentSchema.required) && parentSchema.required.includes(fieldName);
7371
+ },
7372
+ isNullable() {
7373
+ return !!this.schema._nullable;
7374
+ },
7375
+ isNullValue() {
7376
+ return this.modelValue === null || this.modelValue === undefined;
7377
+ },
7378
+ fieldErrors() {
7379
+ if (!this.form || !this.form.getErrorsForPath) return [];
7380
+ return this.form.getErrorsForPath(this.path);
7381
+ },
7382
+ },
7383
+ methods: {
7384
+ onInput(e) {
7385
+ const val = e.target.value;
7386
+ if (val === '') {
7387
+ this.$emit('update:modelValue', this.isNullable ? null : '');
7388
+ return;
7389
+ }
7390
+ // Emit ISO-compatible string. Pydantic accepts both "YYYY-MM-DD"
7391
+ // and "YYYY-MM-DDTHH:mm[:ss]" for date / datetime fields.
7392
+ this.$emit('update:modelValue', val);
7393
+ },
7394
+ humanize(str) {
7395
+ if (!str) return '';
7396
+ return str.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2').replace(/^./, s => s.toUpperCase());
7397
+ },
7398
+ },
7399
+ };
7400
+
7401
+ const _hoisted_1$a = {
7402
+ key: 0,
7403
+ class: "sf-null-badge"
7404
+ };
7405
+ const _hoisted_2$8 = ["type", "value", "placeholder", "min", "max"];
7406
+ const _hoisted_3$8 = {
7407
+ key: 0,
7408
+ class: "errorlist"
7409
+ };
7410
+
7411
+ function render$b(_ctx, _cache, $props, $setup, $data, $options) {
7412
+ return (openBlock(), createElementBlock("div", {
7413
+ class: normalizeClass(["sf-field", { errors: $options.fieldErrors.length }])
7414
+ }, [
7415
+ createBaseVNode("span", {
7416
+ class: normalizeClass(["sf-label", { required: $options.isRequired }])
7417
+ }, [
7418
+ createTextVNode(toDisplayString($options.title) + " ", 1 /* TEXT */),
7419
+ ($options.isNullable && $options.isNullValue)
7420
+ ? (openBlock(), createElementBlock("span", _hoisted_1$a, "null"))
7421
+ : createCommentVNode("v-if", true)
7422
+ ], 2 /* CLASS */),
7423
+ createBaseVNode("div", {
7424
+ class: normalizeClass($options.isNullable ? 'sf-input-row' : null)
7425
+ }, [
7426
+ createBaseVNode("input", {
7427
+ type: $options.inputType,
7428
+ class: "sf-input",
7429
+ value: $options.displayValue,
7430
+ placeholder: $options.isNullValue ? 'null' : ($props.schema.placeholder || ''),
7431
+ min: $props.schema.minimum || $props.schema.formatMinimum || null,
7432
+ max: $props.schema.maximum || $props.schema.formatMaximum || null,
7433
+ onInput: _cache[0] || (_cache[0] = (...args) => ($options.onInput && $options.onInput(...args)))
7434
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$8),
7435
+ ($options.isNullable && !$options.isNullValue)
7436
+ ? (openBlock(), createElementBlock("button", {
7437
+ key: 0,
7438
+ type: "button",
7439
+ class: "sf-null-clear-btn",
7440
+ title: "Set to null",
7441
+ onClick: _cache[1] || (_cache[1] = $event => (_ctx.$emit('update:modelValue', null)))
7442
+ }, "✕"))
7443
+ : createCommentVNode("v-if", true)
7444
+ ], 2 /* CLASS */),
7445
+ ($options.fieldErrors.length)
7446
+ ? (openBlock(), createElementBlock("ul", _hoisted_3$8, [
7447
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.fieldErrors, (err, i) => {
7448
+ return (openBlock(), createElementBlock("li", { key: i }, toDisplayString(err), 1 /* TEXT */))
7449
+ }), 128 /* KEYED_FRAGMENT */))
7450
+ ]))
7451
+ : createCommentVNode("v-if", true)
7452
+ ], 2 /* CLASS */))
7453
+ }
7454
+
7455
+ script$c.render = render$b;
7456
+ script$c.__file = "src/editors/DateEditor.vue";
7457
+
7458
+ function debounce(fn, delay) {
7459
+ let timer;
7460
+ return function (...args) {
7461
+ clearTimeout(timer);
7462
+ timer = setTimeout(() => fn.apply(this, args), delay);
7463
+ };
7464
+ }
7465
+
7466
+ function deepClone(obj) {
7467
+ if (obj === null || typeof obj !== 'object') return obj;
7468
+ if (Array.isArray(obj)) return obj.map(deepClone);
7469
+ const clone = {};
7470
+ for (const key of Object.keys(obj)) {
7471
+ clone[key] = deepClone(obj[key]);
7472
+ }
7473
+ return clone;
7474
+ }
7475
+
7476
+ function isChoiceOneOf(list) {
7477
+ // A "choice list" oneOf: every member is a {const, title?} value option
7478
+ // (the shape emitted for enumerated choices, e.g. metaobjects' select
7479
+ // kind), as opposed to a oneOf of alternative sub-schemas.
7480
+ return (
7481
+ Array.isArray(list) &&
7482
+ list.length > 0 &&
7483
+ list.every(
7484
+ (m) =>
7485
+ m &&
7486
+ typeof m === 'object' &&
7487
+ 'const' in m &&
7488
+ !('properties' in m) &&
7489
+ m.type !== 'null'
7490
+ )
7491
+ );
7492
+ }
7493
+
7494
+ function getDefaultForSchema(schema) {
7495
+ if ('default' in schema) return deepClone(schema.default);
7496
+ if (schema.type === 'object') {
7497
+ const obj = {};
7498
+ for (const [key, prop] of Object.entries(schema.properties || {})) {
7499
+ if ('default' in prop) obj[key] = deepClone(prop.default);
7500
+ else if (prop.type === 'string') obj[key] = '';
7501
+ else if (prop.type === 'integer' || prop.type === 'number') obj[key] = 0;
7502
+ else if (prop.type === 'boolean') obj[key] = false;
7503
+ else if (prop.type === 'array') obj[key] = [];
7504
+ }
7505
+ return obj;
7506
+ }
7507
+ if (schema.type === 'array') return [];
7508
+ if (schema.type === 'string') return '';
7509
+ if (schema.type === 'integer' || schema.type === 'number') return 0;
7510
+ if (schema.type === 'boolean') return false;
7511
+ if (schema.type === 'relation') return schema.multiple ? [] : null;
7512
+ return null;
7513
+ }
7514
+
7515
+ var script$b = {
7250
7516
  name: 'SelectEditor',
7251
7517
  props: {
7252
7518
  schema: { type: Object, required: true },
@@ -7256,6 +7522,20 @@ var script$a = {
7256
7522
  },
7257
7523
  emits: ['update:modelValue'],
7258
7524
  computed: {
7525
+ options() {
7526
+ // Two source shapes: a choice-list oneOf ({const, title} pairs, e.g.
7527
+ // metaobjects' select kind — labels preserved) or a plain enum.
7528
+ if (isChoiceOneOf(this.schema.oneOf)) {
7529
+ return this.schema.oneOf.map((o) => ({
7530
+ value: o.const,
7531
+ label: o.title != null ? o.title : String(o.const),
7532
+ }));
7533
+ }
7534
+ return (this.schema.enum || []).map((v) => ({ value: v, label: String(v) }));
7535
+ },
7536
+ selectedIndex() {
7537
+ return this.options.findIndex((o) => o.value === this.modelValue);
7538
+ },
7259
7539
  title() {
7260
7540
  return this.schema.title || this.humanize(this.path[this.path.length - 1]) || '';
7261
7541
  },
@@ -7266,12 +7546,25 @@ var script$a = {
7266
7546
  const parentSchema = this.form.getSchemaAtPath(parentPath);
7267
7547
  return parentSchema && Array.isArray(parentSchema.required) && parentSchema.required.includes(fieldName);
7268
7548
  },
7549
+ isNullable() {
7550
+ return !!this.schema._nullable;
7551
+ },
7552
+ isNullValue() {
7553
+ return this.modelValue === null || this.modelValue === undefined;
7554
+ },
7269
7555
  fieldErrors() {
7270
7556
  if (!this.form || !this.form.getErrorsForPath) return [];
7271
7557
  return this.form.getErrorsForPath(this.path);
7272
7558
  },
7273
7559
  },
7274
7560
  methods: {
7561
+ onChange(indexStr) {
7562
+ const opt = this.options[Number(indexStr)];
7563
+ // Emit the ORIGINAL option value (options are addressed by index in
7564
+ // the DOM), so integer/boolean enums keep their native type instead
7565
+ // of being stringified by the <select> element.
7566
+ if (opt !== undefined) this.$emit('update:modelValue', opt.value);
7567
+ },
7275
7568
  humanize(str) {
7276
7569
  if (!str) return '';
7277
7570
  return str.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2').replace(/^./, s => s.toUpperCase());
@@ -7279,34 +7572,65 @@ var script$a = {
7279
7572
  },
7280
7573
  };
7281
7574
 
7282
- const _hoisted_1$8 = ["value"];
7283
- const _hoisted_2$6 = ["value"];
7284
- const _hoisted_3$6 = {
7575
+ const _hoisted_1$9 = {
7576
+ key: 0,
7577
+ class: "sf-null-badge"
7578
+ };
7579
+ const _hoisted_2$7 = ["value"];
7580
+ const _hoisted_3$7 = {
7581
+ key: 0,
7582
+ value: "",
7583
+ disabled: "",
7584
+ selected: ""
7585
+ };
7586
+ const _hoisted_4$5 = ["value"];
7587
+ const _hoisted_5$5 = {
7285
7588
  key: 0,
7286
7589
  class: "errorlist"
7287
7590
  };
7288
7591
 
7289
- function render$9(_ctx, _cache, $props, $setup, $data, $options) {
7592
+ function render$a(_ctx, _cache, $props, $setup, $data, $options) {
7290
7593
  return (openBlock(), createElementBlock("div", {
7291
7594
  class: normalizeClass(["sf-field", { errors: $options.fieldErrors.length }])
7292
7595
  }, [
7293
- createBaseVNode("label", {
7596
+ createBaseVNode("span", {
7294
7597
  class: normalizeClass(["sf-label", { required: $options.isRequired }])
7295
- }, toDisplayString($options.title), 3 /* TEXT, CLASS */),
7296
- createBaseVNode("select", {
7297
- class: "sf-input sf-select",
7298
- value: $props.modelValue != null ? String($props.modelValue) : '',
7299
- onChange: _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event.target.value)))
7300
7598
  }, [
7301
- (openBlock(true), createElementBlock(Fragment, null, renderList(($props.schema.enum || []), (opt) => {
7302
- return (openBlock(), createElementBlock("option", {
7303
- key: opt,
7304
- value: String(opt)
7305
- }, toDisplayString(opt), 9 /* TEXT, PROPS */, _hoisted_2$6))
7306
- }), 128 /* KEYED_FRAGMENT */))
7307
- ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_1$8),
7599
+ createTextVNode(toDisplayString($options.title) + " ", 1 /* TEXT */),
7600
+ ($options.isNullable && $options.isNullValue)
7601
+ ? (openBlock(), createElementBlock("span", _hoisted_1$9, "null"))
7602
+ : createCommentVNode("v-if", true)
7603
+ ], 2 /* CLASS */),
7604
+ createBaseVNode("div", {
7605
+ class: normalizeClass($options.isNullable ? 'sf-input-row' : null)
7606
+ }, [
7607
+ createBaseVNode("select", {
7608
+ class: "sf-input sf-select",
7609
+ value: $options.selectedIndex === -1 ? '' : String($options.selectedIndex),
7610
+ onChange: _cache[0] || (_cache[0] = $event => ($options.onChange($event.target.value)))
7611
+ }, [
7612
+ ($options.selectedIndex === -1)
7613
+ ? (openBlock(), createElementBlock("option", _hoisted_3$7, toDisplayString($options.isNullValue ? 'null' : ''), 1 /* TEXT */))
7614
+ : createCommentVNode("v-if", true),
7615
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.options, (opt, i) => {
7616
+ return (openBlock(), createElementBlock("option", {
7617
+ key: i,
7618
+ value: String(i)
7619
+ }, toDisplayString(opt.label), 9 /* TEXT, PROPS */, _hoisted_4$5))
7620
+ }), 128 /* KEYED_FRAGMENT */))
7621
+ ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$7),
7622
+ ($options.isNullable && !$options.isNullValue)
7623
+ ? (openBlock(), createElementBlock("button", {
7624
+ key: 0,
7625
+ type: "button",
7626
+ class: "sf-null-clear-btn",
7627
+ title: "Set to null",
7628
+ onClick: _cache[1] || (_cache[1] = $event => (_ctx.$emit('update:modelValue', null)))
7629
+ }, "✕"))
7630
+ : createCommentVNode("v-if", true)
7631
+ ], 2 /* CLASS */),
7308
7632
  ($options.fieldErrors.length)
7309
- ? (openBlock(), createElementBlock("ul", _hoisted_3$6, [
7633
+ ? (openBlock(), createElementBlock("ul", _hoisted_5$5, [
7310
7634
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.fieldErrors, (err, i) => {
7311
7635
  return (openBlock(), createElementBlock("li", { key: i }, toDisplayString(err), 1 /* TEXT */))
7312
7636
  }), 128 /* KEYED_FRAGMENT */))
@@ -7315,10 +7639,10 @@ function render$9(_ctx, _cache, $props, $setup, $data, $options) {
7315
7639
  ], 2 /* CLASS */))
7316
7640
  }
7317
7641
 
7318
- script$a.render = render$9;
7319
- script$a.__file = "src/editors/SelectEditor.vue";
7642
+ script$b.render = render$a;
7643
+ script$b.__file = "src/editors/SelectEditor.vue";
7320
7644
 
7321
- var script$9 = {
7645
+ var script$a = {
7322
7646
  name: 'HiddenEditor',
7323
7647
  props: {
7324
7648
  schema: { type: Object, required: true },
@@ -7346,16 +7670,16 @@ var script$9 = {
7346
7670
  },
7347
7671
  };
7348
7672
 
7349
- const _hoisted_1$7 = { style: {"display":"none"} };
7673
+ const _hoisted_1$8 = { style: {"display":"none"} };
7350
7674
 
7351
- function render$8(_ctx, _cache, $props, $setup, $data, $options) {
7352
- return (openBlock(), createElementBlock("div", _hoisted_1$7))
7675
+ function render$9(_ctx, _cache, $props, $setup, $data, $options) {
7676
+ return (openBlock(), createElementBlock("div", _hoisted_1$8))
7353
7677
  }
7354
7678
 
7355
- script$9.render = render$8;
7356
- script$9.__file = "src/editors/HiddenEditor.vue";
7679
+ script$a.render = render$9;
7680
+ script$a.__file = "src/editors/HiddenEditor.vue";
7357
7681
 
7358
- var script$8 = {
7682
+ var script$9 = {
7359
7683
  name: 'SfIcon',
7360
7684
  props: {
7361
7685
  name: { type: String, required: true },
@@ -7363,17 +7687,17 @@ var script$8 = {
7363
7687
  },
7364
7688
  };
7365
7689
 
7366
- const _hoisted_1$6 = ["width", "height"];
7367
- const _hoisted_2$5 = {
7690
+ const _hoisted_1$7 = ["width", "height"];
7691
+ const _hoisted_2$6 = {
7368
7692
  key: 4,
7369
7693
  points: "6 9 12 15 18 9"
7370
7694
  };
7371
- const _hoisted_3$5 = {
7695
+ const _hoisted_3$6 = {
7372
7696
  key: 5,
7373
7697
  points: "18 15 12 9 6 15"
7374
7698
  };
7375
7699
 
7376
- function render$7(_ctx, _cache, $props, $setup, $data, $options) {
7700
+ function render$8(_ctx, _cache, $props, $setup, $data, $options) {
7377
7701
  return (openBlock(), createElementBlock("svg", {
7378
7702
  class: "sf-icon",
7379
7703
  width: $props.size,
@@ -7436,19 +7760,19 @@ function render$7(_ctx, _cache, $props, $setup, $data, $options) {
7436
7760
  _cache[7] || (_cache[7] = createBaseVNode("polyline", { points: "19 12 12 19 5 12" }, null, -1 /* CACHED */))
7437
7761
  ], 64 /* STABLE_FRAGMENT */))
7438
7762
  : ($props.name === 'chevron-down')
7439
- ? (openBlock(), createElementBlock("polyline", _hoisted_2$5))
7763
+ ? (openBlock(), createElementBlock("polyline", _hoisted_2$6))
7440
7764
  : ($props.name === 'chevron-up')
7441
- ? (openBlock(), createElementBlock("polyline", _hoisted_3$5))
7765
+ ? (openBlock(), createElementBlock("polyline", _hoisted_3$6))
7442
7766
  : ($props.name === 'grip')
7443
7767
  ? (openBlock(), createElementBlock(Fragment, { key: 6 }, [
7444
7768
  _cache[8] || (_cache[8] = createStaticVNode("<circle cx=\"9\" cy=\"7\" r=\"1\" fill=\"currentColor\" stroke=\"none\"></circle><circle cx=\"15\" cy=\"7\" r=\"1\" fill=\"currentColor\" stroke=\"none\"></circle><circle cx=\"9\" cy=\"12\" r=\"1\" fill=\"currentColor\" stroke=\"none\"></circle><circle cx=\"15\" cy=\"12\" r=\"1\" fill=\"currentColor\" stroke=\"none\"></circle><circle cx=\"9\" cy=\"17\" r=\"1\" fill=\"currentColor\" stroke=\"none\"></circle><circle cx=\"15\" cy=\"17\" r=\"1\" fill=\"currentColor\" stroke=\"none\"></circle>", 6))
7445
7769
  ], 64 /* STABLE_FRAGMENT */))
7446
7770
  : createCommentVNode("v-if", true)
7447
- ], 8 /* PROPS */, _hoisted_1$6))
7771
+ ], 8 /* PROPS */, _hoisted_1$7))
7448
7772
  }
7449
7773
 
7450
- script$8.render = render$7;
7451
- script$8.__file = "src/editors/SfIcon.vue";
7774
+ script$9.render = render$8;
7775
+ script$9.__file = "src/editors/SfIcon.vue";
7452
7776
 
7453
7777
  // JSON Schema conditional evaluation for form rendering.
7454
7778
  //
@@ -7699,12 +8023,12 @@ function hasConditionals(schema) {
7699
8023
  return false;
7700
8024
  }
7701
8025
 
7702
- var script$7 = {
8026
+ var script$8 = {
7703
8027
  name: 'ObjectEditor',
7704
8028
  beforeCreate() {
7705
8029
  if (!this.$options.components) this.$options.components = {};
7706
8030
  this.$options.components.SchemaEditor = script$1;
7707
- this.$options.components.SfIcon = script$8;
8031
+ this.$options.components.SfIcon = script$9;
7708
8032
  },
7709
8033
  props: {
7710
8034
  schema: { type: Object, required: true },
@@ -7716,6 +8040,9 @@ var script$7 = {
7716
8040
  data() {
7717
8041
  return {
7718
8042
  collapsed: false,
8043
+ // Values pruned when a conditional rule deactivated their field,
8044
+ // kept so toggling the controller back restores what the user typed.
8045
+ prunedStash: {},
7719
8046
  };
7720
8047
  },
7721
8048
  computed: {
@@ -7763,10 +8090,21 @@ var script$7 = {
7763
8090
  const allowed = new Set(Object.keys(effective.properties || {}));
7764
8091
  let changed = false;
7765
8092
  const out = {};
8093
+ // restore stashed values for fields a rule just re-activated
8094
+ for (const k of allowed) {
8095
+ if (!(k in value) && k in this.prunedStash) {
8096
+ out[k] = this.prunedStash[k];
8097
+ delete this.prunedStash[k];
8098
+ changed = true;
8099
+ }
8100
+ }
7766
8101
  for (const k of Object.keys(value)) {
7767
8102
  if (allowed.has(k)) {
7768
8103
  out[k] = value[k];
7769
8104
  } else {
8105
+ // pruned from the emitted value (documented behavior), but kept
8106
+ // locally so a controller toggle round-trip is not destructive
8107
+ this.prunedStash[k] = value[k];
7770
8108
  changed = true;
7771
8109
  }
7772
8110
  }
@@ -7775,27 +8113,27 @@ var script$7 = {
7775
8113
  },
7776
8114
  };
7777
8115
 
7778
- const _hoisted_1$5 = {
8116
+ const _hoisted_1$6 = {
7779
8117
  key: 0,
7780
8118
  class: "sf-object sf-object-root"
7781
8119
  };
7782
- const _hoisted_2$4 = { class: "sf-object-fields" };
7783
- const _hoisted_3$4 = { class: "sf-object-title" };
7784
- const _hoisted_4$3 = ["aria-label"];
7785
- const _hoisted_5$3 = { class: "sf-object-title-text" };
8120
+ const _hoisted_2$5 = { class: "sf-object-fields" };
8121
+ const _hoisted_3$5 = { class: "sf-object-title" };
8122
+ const _hoisted_4$4 = ["aria-label"];
8123
+ const _hoisted_5$4 = { class: "sf-object-title-text" };
7786
8124
  const _hoisted_6$3 = {
7787
8125
  key: 0,
7788
8126
  class: "sf-object-summary"
7789
8127
  };
7790
8128
  const _hoisted_7$2 = { class: "sf-object-fields" };
7791
8129
 
7792
- function render$6(_ctx, _cache, $props, $setup, $data, $options) {
8130
+ function render$7(_ctx, _cache, $props, $setup, $data, $options) {
7793
8131
  const _component_SchemaEditor = resolveComponent("SchemaEditor");
7794
8132
  const _component_SfIcon = resolveComponent("SfIcon");
7795
8133
 
7796
8134
  return ($options.isRoot)
7797
- ? (openBlock(), createElementBlock("div", _hoisted_1$5, [
7798
- createBaseVNode("div", _hoisted_2$4, [
8135
+ ? (openBlock(), createElementBlock("div", _hoisted_1$6, [
8136
+ createBaseVNode("div", _hoisted_2$5, [
7799
8137
  (openBlock(true), createElementBlock(Fragment, null, renderList(($options.effectiveSchema.properties || {}), (propSchema, key) => {
7800
8138
  return (openBlock(), createBlock(_component_SchemaEditor, {
7801
8139
  key: key,
@@ -7812,7 +8150,7 @@ function render$6(_ctx, _cache, $props, $setup, $data, $options) {
7812
8150
  key: 1,
7813
8151
  class: normalizeClass(["sf-object", { 'sf-object-collapsed': $data.collapsed }])
7814
8152
  }, [
7815
- createBaseVNode("legend", _hoisted_3$4, [
8153
+ createBaseVNode("legend", _hoisted_3$5, [
7816
8154
  createBaseVNode("button", {
7817
8155
  type: "button",
7818
8156
  class: "sf-collapse-btn",
@@ -7823,8 +8161,8 @@ function render$6(_ctx, _cache, $props, $setup, $data, $options) {
7823
8161
  name: $data.collapsed ? 'chevron-down' : 'chevron-up',
7824
8162
  size: 12
7825
8163
  }, null, 8 /* PROPS */, ["name"])
7826
- ], 8 /* PROPS */, _hoisted_4$3),
7827
- createBaseVNode("span", _hoisted_5$3, toDisplayString($options.title), 1 /* TEXT */),
8164
+ ], 8 /* PROPS */, _hoisted_4$4),
8165
+ createBaseVNode("span", _hoisted_5$4, toDisplayString($options.title), 1 /* TEXT */),
7828
8166
  ($data.collapsed && $options.summary)
7829
8167
  ? (openBlock(), createElementBlock("span", _hoisted_6$3, toDisplayString($options.summary), 1 /* TEXT */))
7830
8168
  : createCommentVNode("v-if", true)
@@ -7846,56 +8184,17 @@ function render$6(_ctx, _cache, $props, $setup, $data, $options) {
7846
8184
  ], 2 /* CLASS */))
7847
8185
  }
7848
8186
 
7849
- script$7.render = render$6;
7850
- script$7.__file = "src/editors/ObjectEditor.vue";
7851
-
7852
- function debounce(fn, delay) {
7853
- let timer;
7854
- return function (...args) {
7855
- clearTimeout(timer);
7856
- timer = setTimeout(() => fn.apply(this, args), delay);
7857
- };
7858
- }
7859
-
7860
- function deepClone(obj) {
7861
- if (obj === null || typeof obj !== 'object') return obj;
7862
- if (Array.isArray(obj)) return obj.map(deepClone);
7863
- const clone = {};
7864
- for (const key of Object.keys(obj)) {
7865
- clone[key] = deepClone(obj[key]);
7866
- }
7867
- return clone;
7868
- }
7869
-
7870
- function getDefaultForSchema(schema) {
7871
- if ('default' in schema) return deepClone(schema.default);
7872
- if (schema.type === 'object') {
7873
- const obj = {};
7874
- for (const [key, prop] of Object.entries(schema.properties || {})) {
7875
- if ('default' in prop) obj[key] = deepClone(prop.default);
7876
- else if (prop.type === 'string') obj[key] = '';
7877
- else if (prop.type === 'integer' || prop.type === 'number') obj[key] = 0;
7878
- else if (prop.type === 'boolean') obj[key] = false;
7879
- else if (prop.type === 'array') obj[key] = [];
7880
- }
7881
- return obj;
7882
- }
7883
- if (schema.type === 'array') return [];
7884
- if (schema.type === 'string') return '';
7885
- if (schema.type === 'integer' || schema.type === 'number') return 0;
7886
- if (schema.type === 'boolean') return false;
7887
- if (schema.type === 'relation') return schema.multiple ? [] : null;
7888
- return null;
7889
- }
8187
+ script$8.render = render$7;
8188
+ script$8.__file = "src/editors/ObjectEditor.vue";
7890
8189
 
7891
8190
  let keyCounter = 0;
7892
8191
 
7893
- var script$6 = {
8192
+ var script$7 = {
7894
8193
  name: 'ArrayEditor',
7895
8194
  beforeCreate() {
7896
8195
  if (!this.$options.components) this.$options.components = {};
7897
8196
  this.$options.components.SchemaEditor = script$1;
7898
- this.$options.components.SfIcon = script$8;
8197
+ this.$options.components.SfIcon = script$9;
7899
8198
  },
7900
8199
  props: {
7901
8200
  schema: { type: Object, required: true },
@@ -7995,11 +8294,11 @@ var script$6 = {
7995
8294
  },
7996
8295
  };
7997
8296
 
7998
- const _hoisted_1$4 = { class: "sf-array-header" };
7999
- const _hoisted_2$3 = { class: "sf-label" };
8000
- const _hoisted_3$3 = { class: "sf-array-count" };
8001
- const _hoisted_4$2 = ["title"];
8002
- const _hoisted_5$2 = { class: "sf-array-items" };
8297
+ const _hoisted_1$5 = { class: "sf-array-header" };
8298
+ const _hoisted_2$4 = { class: "sf-label" };
8299
+ const _hoisted_3$4 = { class: "sf-array-count" };
8300
+ const _hoisted_4$3 = ["title"];
8301
+ const _hoisted_5$3 = { class: "sf-array-items" };
8003
8302
  const _hoisted_6$2 = ["onDragstart", "onDragover", "onDragleave", "onDrop"];
8004
8303
  const _hoisted_7$1 = { class: "sf-array-item-header" };
8005
8304
  const _hoisted_8$1 = { class: "sf-array-item-left" };
@@ -8018,16 +8317,16 @@ const _hoisted_16 = {
8018
8317
  class: "errorlist"
8019
8318
  };
8020
8319
 
8021
- function render$5(_ctx, _cache, $props, $setup, $data, $options) {
8320
+ function render$6(_ctx, _cache, $props, $setup, $data, $options) {
8022
8321
  const _component_SfIcon = resolveComponent("SfIcon");
8023
8322
  const _component_SchemaEditor = resolveComponent("SchemaEditor");
8024
8323
 
8025
8324
  return (openBlock(), createElementBlock("div", {
8026
8325
  class: normalizeClass(["sf-array", { errors: $options.fieldErrors.length }])
8027
8326
  }, [
8028
- createBaseVNode("div", _hoisted_1$4, [
8029
- createBaseVNode("span", _hoisted_2$3, toDisplayString($options.title), 1 /* TEXT */),
8030
- createBaseVNode("span", _hoisted_3$3, toDisplayString($data.items.length), 1 /* TEXT */),
8327
+ createBaseVNode("div", _hoisted_1$5, [
8328
+ createBaseVNode("span", _hoisted_2$4, toDisplayString($options.title), 1 /* TEXT */),
8329
+ createBaseVNode("span", _hoisted_3$4, toDisplayString($data.items.length), 1 /* TEXT */),
8031
8330
  createBaseVNode("button", {
8032
8331
  type: "button",
8033
8332
  class: "sf-btn sf-btn-add",
@@ -8046,10 +8345,10 @@ function render$5(_ctx, _cache, $props, $setup, $data, $options) {
8046
8345
  createVNode(_component_SfIcon, {
8047
8346
  name: $data.allCollapsed ? 'chevron-down' : 'chevron-up'
8048
8347
  }, null, 8 /* PROPS */, ["name"])
8049
- ], 8 /* PROPS */, _hoisted_4$2))
8348
+ ], 8 /* PROPS */, _hoisted_4$3))
8050
8349
  : createCommentVNode("v-if", true)
8051
8350
  ]),
8052
- createBaseVNode("div", _hoisted_5$2, [
8351
+ createBaseVNode("div", _hoisted_5$3, [
8053
8352
  (openBlock(true), createElementBlock(Fragment, null, renderList($data.items, (item, index) => {
8054
8353
  return (openBlock(), createElementBlock("div", {
8055
8354
  key: item._key,
@@ -8126,15 +8425,15 @@ function render$5(_ctx, _cache, $props, $setup, $data, $options) {
8126
8425
  ], 2 /* CLASS */))
8127
8426
  }
8128
8427
 
8129
- script$6.render = render$5;
8130
- script$6.__file = "src/editors/ArrayEditor.vue";
8428
+ script$7.render = render$6;
8429
+ script$7.__file = "src/editors/ArrayEditor.vue";
8131
8430
 
8132
- var script$5 = {
8431
+ var script$6 = {
8133
8432
  name: 'NullableEditor',
8134
8433
  beforeCreate() {
8135
8434
  if (!this.$options.components) this.$options.components = {};
8136
8435
  this.$options.components.SchemaEditor = script$1;
8137
- this.$options.components.SfIcon = script$8;
8436
+ this.$options.components.SfIcon = script$9;
8138
8437
  },
8139
8438
  props: {
8140
8439
  schema: { type: Object, required: true },
@@ -8194,22 +8493,22 @@ var script$5 = {
8194
8493
  },
8195
8494
  };
8196
8495
 
8197
- const _hoisted_1$3 = { class: "sf-nullable-header" };
8198
- const _hoisted_2$2 = { class: "sf-nullable-body" };
8199
- const _hoisted_3$2 = {
8496
+ const _hoisted_1$4 = { class: "sf-nullable-header" };
8497
+ const _hoisted_2$3 = { class: "sf-nullable-body" };
8498
+ const _hoisted_3$3 = {
8200
8499
  key: 0,
8201
8500
  class: "errorlist"
8202
8501
  };
8203
8502
 
8204
- function render$4(_ctx, _cache, $props, $setup, $data, $options) {
8503
+ function render$5(_ctx, _cache, $props, $setup, $data, $options) {
8205
8504
  const _component_SfIcon = resolveComponent("SfIcon");
8206
8505
  const _component_SchemaEditor = resolveComponent("SchemaEditor");
8207
8506
 
8208
8507
  return (openBlock(), createElementBlock("div", {
8209
8508
  class: normalizeClass(["sf-nullable", { errors: $options.fieldErrors.length }])
8210
8509
  }, [
8211
- createBaseVNode("div", _hoisted_1$3, [
8212
- createBaseVNode("label", {
8510
+ createBaseVNode("div", _hoisted_1$4, [
8511
+ createBaseVNode("span", {
8213
8512
  class: normalizeClass(["sf-label", { required: $options.isRequired }])
8214
8513
  }, toDisplayString($options.title), 3 /* TEXT, CLASS */),
8215
8514
  createBaseVNode("button", {
@@ -8228,7 +8527,7 @@ function render$4(_ctx, _cache, $props, $setup, $data, $options) {
8228
8527
  ], 64 /* STABLE_FRAGMENT */))
8229
8528
  ], 2 /* CLASS */)
8230
8529
  ]),
8231
- createBaseVNode("div", _hoisted_2$2, [
8530
+ createBaseVNode("div", _hoisted_2$3, [
8232
8531
  (!$data.isNull)
8233
8532
  ? (openBlock(), createBlock(_component_SchemaEditor, {
8234
8533
  key: 0,
@@ -8241,7 +8540,7 @@ function render$4(_ctx, _cache, $props, $setup, $data, $options) {
8241
8540
  : createCommentVNode("v-if", true)
8242
8541
  ]),
8243
8542
  ($options.fieldErrors.length)
8244
- ? (openBlock(), createElementBlock("ul", _hoisted_3$2, [
8543
+ ? (openBlock(), createElementBlock("ul", _hoisted_3$3, [
8245
8544
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.fieldErrors, (err, i) => {
8246
8545
  return (openBlock(), createElementBlock("li", { key: i }, toDisplayString(err), 1 /* TEXT */))
8247
8546
  }), 128 /* KEYED_FRAGMENT */))
@@ -8250,10 +8549,10 @@ function render$4(_ctx, _cache, $props, $setup, $data, $options) {
8250
8549
  ], 2 /* CLASS */))
8251
8550
  }
8252
8551
 
8253
- script$5.render = render$4;
8254
- script$5.__file = "src/editors/NullableEditor.vue";
8552
+ script$6.render = render$5;
8553
+ script$6.__file = "src/editors/NullableEditor.vue";
8255
8554
 
8256
- var script$4 = {
8555
+ var script$5 = {
8257
8556
  name: 'UnionEditor',
8258
8557
  beforeCreate() {
8259
8558
  if (!this.$options.components) this.$options.components = {};
@@ -8314,19 +8613,19 @@ var script$4 = {
8314
8613
  },
8315
8614
  };
8316
8615
 
8317
- const _hoisted_1$2 = { class: "sf-union" };
8318
- const _hoisted_2$1 = { class: "sf-field" };
8319
- const _hoisted_3$1 = { class: "sf-label" };
8320
- const _hoisted_4$1 = ["value"];
8321
- const _hoisted_5$1 = ["value"];
8616
+ const _hoisted_1$3 = { class: "sf-union" };
8617
+ const _hoisted_2$2 = { class: "sf-field" };
8618
+ const _hoisted_3$2 = { class: "sf-label" };
8619
+ const _hoisted_4$2 = ["value"];
8620
+ const _hoisted_5$2 = ["value"];
8322
8621
  const _hoisted_6$1 = { class: "sf-union-body" };
8323
8622
 
8324
- function render$3(_ctx, _cache, $props, $setup, $data, $options) {
8623
+ function render$4(_ctx, _cache, $props, $setup, $data, $options) {
8325
8624
  const _component_SchemaEditor = resolveComponent("SchemaEditor");
8326
8625
 
8327
- return (openBlock(), createElementBlock("div", _hoisted_1$2, [
8328
- createBaseVNode("div", _hoisted_2$1, [
8329
- createBaseVNode("label", _hoisted_3$1, toDisplayString($options.title), 1 /* TEXT */),
8626
+ return (openBlock(), createElementBlock("div", _hoisted_1$3, [
8627
+ createBaseVNode("div", _hoisted_2$2, [
8628
+ createBaseVNode("span", _hoisted_3$2, toDisplayString($options.title), 1 /* TEXT */),
8330
8629
  createBaseVNode("select", {
8331
8630
  class: "sf-input sf-select",
8332
8631
  value: $data.currentType,
@@ -8336,9 +8635,9 @@ function render$3(_ctx, _cache, $props, $setup, $data, $options) {
8336
8635
  return (openBlock(), createElementBlock("option", {
8337
8636
  key: key,
8338
8637
  value: key
8339
- }, toDisplayString($options.humanize(key)), 9 /* TEXT, PROPS */, _hoisted_5$1))
8638
+ }, toDisplayString($options.humanize(key)), 9 /* TEXT, PROPS */, _hoisted_5$2))
8340
8639
  }), 128 /* KEYED_FRAGMENT */))
8341
- ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_4$1)
8640
+ ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_4$2)
8342
8641
  ]),
8343
8642
  createBaseVNode("div", _hoisted_6$1, [
8344
8643
  (openBlock(), createBlock(_component_SchemaEditor, {
@@ -8353,12 +8652,12 @@ function render$3(_ctx, _cache, $props, $setup, $data, $options) {
8353
8652
  ]))
8354
8653
  }
8355
8654
 
8356
- script$4.render = render$3;
8357
- script$4.__file = "src/editors/UnionEditor.vue";
8655
+ script$5.render = render$4;
8656
+ script$5.__file = "src/editors/UnionEditor.vue";
8358
8657
 
8359
- var script$3 = {
8658
+ var script$4 = {
8360
8659
  name: 'RelationEditor',
8361
- components: { SfIcon: script$8 },
8660
+ components: { SfIcon: script$9 },
8362
8661
  inject: {
8363
8662
  language: { from: 'language', default: () => () => '' },
8364
8663
  },
@@ -8526,14 +8825,14 @@ var script$3 = {
8526
8825
  },
8527
8826
  };
8528
8827
 
8529
- const _hoisted_1$1 = { class: "sf-relation-wrapper" };
8530
- const _hoisted_2 = {
8828
+ const _hoisted_1$2 = { class: "sf-relation-wrapper" };
8829
+ const _hoisted_2$1 = {
8531
8830
  key: 0,
8532
8831
  class: "sf-relation-selected"
8533
8832
  };
8534
- const _hoisted_3 = { class: "sf-relation-tag-text" };
8535
- const _hoisted_4 = ["onClick"];
8536
- const _hoisted_5 = { class: "sf-relation-search" };
8833
+ const _hoisted_3$1 = { class: "sf-relation-tag-text" };
8834
+ const _hoisted_4$1 = ["onClick"];
8835
+ const _hoisted_5$1 = { class: "sf-relation-search" };
8537
8836
  const _hoisted_6 = ["placeholder"];
8538
8837
  const _hoisted_7 = { class: "sf-relation-dropdown" };
8539
8838
  const _hoisted_8 = {
@@ -8546,26 +8845,26 @@ const _hoisted_10 = {
8546
8845
  class: "errorlist"
8547
8846
  };
8548
8847
 
8549
- function render$2(_ctx, _cache, $props, $setup, $data, $options) {
8848
+ function render$3(_ctx, _cache, $props, $setup, $data, $options) {
8550
8849
  const _component_SfIcon = resolveComponent("SfIcon");
8551
8850
 
8552
8851
  return (openBlock(), createElementBlock("div", {
8553
8852
  class: normalizeClass(["sf-field sf-relation", { errors: $options.fieldErrors.length }]),
8554
8853
  ref: "root"
8555
8854
  }, [
8556
- createBaseVNode("label", {
8855
+ createBaseVNode("span", {
8557
8856
  class: normalizeClass(["sf-label", { required: $options.isRequired }])
8558
8857
  }, toDisplayString($options.title), 3 /* TEXT, CLASS */),
8559
- createBaseVNode("div", _hoisted_1$1, [
8858
+ createBaseVNode("div", _hoisted_1$2, [
8560
8859
  createCommentVNode(" Selected items "),
8561
8860
  ($data.selected.length)
8562
- ? (openBlock(), createElementBlock("div", _hoisted_2, [
8861
+ ? (openBlock(), createElementBlock("div", _hoisted_2$1, [
8563
8862
  (openBlock(true), createElementBlock(Fragment, null, renderList($data.selected, (item) => {
8564
8863
  return (openBlock(), createElementBlock("div", {
8565
8864
  key: $options.itemKey(item),
8566
8865
  class: "sf-relation-tag"
8567
8866
  }, [
8568
- createBaseVNode("span", _hoisted_3, toDisplayString($options.getDisplayName(item)), 1 /* TEXT */),
8867
+ createBaseVNode("span", _hoisted_3$1, toDisplayString($options.getDisplayName(item)), 1 /* TEXT */),
8569
8868
  ($data.isMultiple || $data.allowClear)
8570
8869
  ? (openBlock(), createElementBlock("button", {
8571
8870
  key: 0,
@@ -8574,14 +8873,14 @@ function render$2(_ctx, _cache, $props, $setup, $data, $options) {
8574
8873
  onClick: withModifiers($event => ($options.removeItem(item)), ["stop"])
8575
8874
  }, [
8576
8875
  createVNode(_component_SfIcon, { name: "times" })
8577
- ], 8 /* PROPS */, _hoisted_4))
8876
+ ], 8 /* PROPS */, _hoisted_4$1))
8578
8877
  : createCommentVNode("v-if", true)
8579
8878
  ]))
8580
8879
  }), 128 /* KEYED_FRAGMENT */))
8581
8880
  ]))
8582
8881
  : createCommentVNode("v-if", true),
8583
8882
  createCommentVNode(" Search box "),
8584
- withDirectives(createBaseVNode("div", _hoisted_5, [
8883
+ withDirectives(createBaseVNode("div", _hoisted_5$1, [
8585
8884
  withDirectives(createBaseVNode("input", {
8586
8885
  ref: "searchInput",
8587
8886
  type: "text",
@@ -8631,8 +8930,217 @@ function render$2(_ctx, _cache, $props, $setup, $data, $options) {
8631
8930
  ], 2 /* CLASS */))
8632
8931
  }
8633
8932
 
8933
+ script$4.render = render$3;
8934
+ script$4.__file = "src/editors/RelationEditor.vue";
8935
+
8936
+ const ACE_CDN = 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.37.5/ace.min.js';
8937
+
8938
+ function loadAce() {
8939
+ if (window.ace) return Promise.resolve(window.ace);
8940
+ return new Promise((resolve, reject) => {
8941
+ const existing = document.querySelector(`script[src="${ACE_CDN}"]`);
8942
+ if (existing) {
8943
+ existing.addEventListener('load', () => resolve(window.ace));
8944
+ existing.addEventListener('error', reject);
8945
+ return;
8946
+ }
8947
+ const s = document.createElement('script');
8948
+ s.src = ACE_CDN;
8949
+ s.async = true;
8950
+ s.onload = () => resolve(window.ace);
8951
+ s.onerror = () => reject(new Error('Failed to load Ace Editor'));
8952
+ document.head.appendChild(s);
8953
+ });
8954
+ }
8955
+
8956
+ var script$3 = {
8957
+ name: 'JsonEditor',
8958
+ props: {
8959
+ schema: { type: Object, required: true },
8960
+ modelValue: { default: null },
8961
+ path: { type: Array, default: () => [] },
8962
+ form: { type: Object, default: null },
8963
+ },
8964
+ emits: ['update:modelValue'],
8965
+ data() {
8966
+ return {
8967
+ rawValue: this.modelValue != null ? JSON.stringify(this.modelValue, null, 2) : '{}',
8968
+ ready: false,
8969
+ hasErrors: false,
8970
+ loadError: null,
8971
+ _editor: null,
8972
+ _silent: false,
8973
+ };
8974
+ },
8975
+ computed: {
8976
+ title() {
8977
+ return this.schema.title || this.humanize(this.path[this.path.length - 1]) || '';
8978
+ },
8979
+ isRequired() {
8980
+ if (this.path.length < 2 || !this.form) return false;
8981
+ const parentPath = this.path.slice(0, -1);
8982
+ const fieldName = this.path[this.path.length - 1];
8983
+ const parentSchema = this.form.getSchemaAtPath(parentPath);
8984
+ return parentSchema && Array.isArray(parentSchema.required) && parentSchema.required.includes(fieldName);
8985
+ },
8986
+ fieldErrors() {
8987
+ if (!this.form || !this.form.getErrorsForPath) return [];
8988
+ return this.form.getErrorsForPath(this.path);
8989
+ },
8990
+ },
8991
+ watch: {
8992
+ modelValue(val) {
8993
+ if (this._silent) return;
8994
+ const external = val != null ? JSON.stringify(val, null, 2) : '{}';
8995
+ try {
8996
+ if (JSON.stringify(JSON.parse(this.rawValue)) !== JSON.stringify(val)) {
8997
+ this.rawValue = external;
8998
+ if (this._editor) {
8999
+ this._silent = true;
9000
+ this._editor.setValue(external, -1);
9001
+ this._silent = false;
9002
+ }
9003
+ }
9004
+ } catch { /* rawValue is invalid, leave it */ }
9005
+ },
9006
+ },
9007
+ async mounted() {
9008
+ try {
9009
+ const ace = await loadAce();
9010
+ const container = this.$refs.aceContainer;
9011
+ if (!container) return;
9012
+
9013
+ const editor = ace.edit(container);
9014
+ editor.setTheme(this._isDark() ? 'ace/theme/one_dark' : 'ace/theme/chrome');
9015
+ editor.session.setMode('ace/mode/json');
9016
+ editor.session.setTabSize(2);
9017
+ editor.session.setUseSoftTabs(true);
9018
+ editor.setShowPrintMargin(false);
9019
+ editor.setOption('minLines', 5);
9020
+ editor.setOption('maxLines', 20);
9021
+ editor.setValue(this.rawValue, -1);
9022
+ editor.$blockScrolling = Infinity;
9023
+
9024
+ editor.session.on('change', () => {
9025
+ if (this._silent) return;
9026
+ const text = editor.getValue();
9027
+ this.rawValue = text;
9028
+ try {
9029
+ const parsed = JSON.parse(text);
9030
+ this.hasErrors = false;
9031
+ this._silent = true;
9032
+ this.$emit('update:modelValue', parsed);
9033
+ this._silent = false;
9034
+ } catch {
9035
+ this.hasErrors = true;
9036
+ }
9037
+ });
9038
+
9039
+ // Reflect annotation (lint) errors on hasErrors
9040
+ editor.session.on('changeAnnotation', () => {
9041
+ const annotations = editor.session.getAnnotations();
9042
+ this.hasErrors = annotations.some(a => a.type === 'error');
9043
+ });
9044
+
9045
+ this._editor = editor;
9046
+ this.ready = true;
9047
+ } catch (err) {
9048
+ this.loadError = `Editor unavailable: ${err.message}`;
9049
+ }
9050
+ },
9051
+ beforeUnmount() {
9052
+ if (this._editor) {
9053
+ this._editor.destroy();
9054
+ this._editor = null;
9055
+ }
9056
+ },
9057
+ methods: {
9058
+ humanize(str) {
9059
+ if (!str) return '';
9060
+ return str.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2').replace(/^./, s => s.toUpperCase());
9061
+ },
9062
+ _isDark() {
9063
+ return document.documentElement.dataset.colorScheme === 'dark' ||
9064
+ window.matchMedia?.('(prefers-color-scheme: dark)').matches;
9065
+ },
9066
+ format() {
9067
+ if (!this._editor) return;
9068
+ try {
9069
+ const formatted = JSON.stringify(JSON.parse(this._editor.getValue()), null, 2);
9070
+ this._silent = true;
9071
+ this._editor.setValue(formatted, -1);
9072
+ this._silent = false;
9073
+ this.rawValue = formatted;
9074
+ } catch { /* invalid JSON */ }
9075
+ },
9076
+ onFallbackInput(e) {
9077
+ this.rawValue = e.target.value;
9078
+ try { this.$emit('update:modelValue', JSON.parse(this.rawValue)); } catch { /* ignore */ }
9079
+ },
9080
+ },
9081
+ };
9082
+
9083
+ const _hoisted_1$1 = { class: "sf-json-toolbar" };
9084
+ const _hoisted_2 = {
9085
+ key: 0,
9086
+ class: "sf-json-error-msg"
9087
+ };
9088
+ const _hoisted_3 = {
9089
+ ref: "aceContainer",
9090
+ class: "sf-json-ace-container"
9091
+ };
9092
+ const _hoisted_4 = ["value"];
9093
+ const _hoisted_5 = {
9094
+ key: 0,
9095
+ class: "errorlist"
9096
+ };
9097
+
9098
+ function render$2(_ctx, _cache, $props, $setup, $data, $options) {
9099
+ return (openBlock(), createElementBlock("div", {
9100
+ class: normalizeClass(["sf-field sf-field-json", { errors: $options.fieldErrors.length }])
9101
+ }, [
9102
+ createBaseVNode("span", {
9103
+ class: normalizeClass(["sf-label", { required: $options.isRequired }])
9104
+ }, toDisplayString($options.title), 3 /* TEXT, CLASS */),
9105
+ createBaseVNode("div", {
9106
+ class: normalizeClass(["sf-json-editor", { 'sf-json-error': $data.hasErrors }])
9107
+ }, [
9108
+ createBaseVNode("div", _hoisted_1$1, [
9109
+ ($data.loadError)
9110
+ ? (openBlock(), createElementBlock("span", _hoisted_2, toDisplayString($data.loadError), 1 /* TEXT */))
9111
+ : createCommentVNode("v-if", true),
9112
+ ($data.ready)
9113
+ ? (openBlock(), createElementBlock("button", {
9114
+ key: 1,
9115
+ type: "button",
9116
+ class: "sf-btn sf-btn-sm sf-json-format-btn",
9117
+ onClick: _cache[0] || (_cache[0] = (...args) => ($options.format && $options.format(...args)))
9118
+ }, " Format "))
9119
+ : createCommentVNode("v-if", true)
9120
+ ]),
9121
+ createBaseVNode("div", _hoisted_3, null, 512 /* NEED_PATCH */),
9122
+ (!$data.ready)
9123
+ ? (openBlock(), createElementBlock("textarea", {
9124
+ key: 0,
9125
+ class: "sf-input sf-textarea sf-json-textarea-fallback",
9126
+ value: $data.rawValue,
9127
+ spellcheck: "false",
9128
+ onInput: _cache[1] || (_cache[1] = (...args) => ($options.onFallbackInput && $options.onFallbackInput(...args)))
9129
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_4))
9130
+ : createCommentVNode("v-if", true)
9131
+ ], 2 /* CLASS */),
9132
+ ($options.fieldErrors.length)
9133
+ ? (openBlock(), createElementBlock("ul", _hoisted_5, [
9134
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.fieldErrors, (err, i) => {
9135
+ return (openBlock(), createElementBlock("li", { key: i }, toDisplayString(err), 1 /* TEXT */))
9136
+ }), 128 /* KEYED_FRAGMENT */))
9137
+ ]))
9138
+ : createCommentVNode("v-if", true)
9139
+ ], 2 /* CLASS */))
9140
+ }
9141
+
8634
9142
  script$3.render = render$2;
8635
- script$3.__file = "src/editors/RelationEditor.vue";
9143
+ script$3.__file = "src/editors/JsonEditor.vue";
8636
9144
 
8637
9145
  var script$2 = {
8638
9146
  name: 'WebComponentWrapper',
@@ -8693,16 +9201,18 @@ const MAX_DEPTH = 12;
8693
9201
  var script$1 = {
8694
9202
  name: 'SchemaEditor',
8695
9203
  components: {
8696
- StringEditor: script$d,
8697
- NumberEditor: script$c,
8698
- BooleanEditor: script$b,
8699
- SelectEditor: script$a,
8700
- HiddenEditor: script$9,
8701
- ObjectEditor: script$7,
8702
- ArrayEditor: script$6,
8703
- NullableEditor: script$5,
8704
- UnionEditor: script$4,
8705
- RelationEditor: script$3,
9204
+ StringEditor: script$f,
9205
+ NumberEditor: script$e,
9206
+ BooleanEditor: script$d,
9207
+ DateEditor: script$c,
9208
+ SelectEditor: script$b,
9209
+ HiddenEditor: script$a,
9210
+ ObjectEditor: script$8,
9211
+ ArrayEditor: script$7,
9212
+ NullableEditor: script$6,
9213
+ UnionEditor: script$5,
9214
+ RelationEditor: script$4,
9215
+ JsonEditor: script$3,
8706
9216
  WebComponentWrapper: script$2,
8707
9217
  },
8708
9218
  inject: {
@@ -8744,14 +9254,19 @@ var script$1 = {
8744
9254
 
8745
9255
  if (schema.type === 'relation') return 'RelationEditor';
8746
9256
  if (schema.oneOf && schema.discriminator) return 'UnionEditor';
9257
+ // Choice-list oneOf ({const, title} options) renders as a select —
9258
+ // must be checked before the 'const' HiddenEditor routing.
9259
+ if (isChoiceOneOf(schema.oneOf)) return 'SelectEditor';
8747
9260
  if ('const' in schema) return 'HiddenEditor';
8748
9261
  if (schema.enum && schema.enum.length === 1 && schema.type === 'string') return 'HiddenEditor';
8749
- if (schema._nullable) return 'NullableEditor';
9262
+ if (schema._nullable && (schema.type === 'object' || schema.type === 'array')) return 'NullableEditor';
8750
9263
  if (schema.type === 'object' && schema.properties) return 'ObjectEditor';
9264
+ if (schema.type === 'object') return 'JsonEditor';
8751
9265
  if (schema.type === 'array') return 'ArrayEditor';
8752
9266
  if (schema.enum) return 'SelectEditor';
8753
9267
  if (schema.type === 'boolean') return 'BooleanEditor';
8754
9268
  if (schema.type === 'number' || schema.type === 'integer') return 'NumberEditor';
9269
+ if (schema.type === 'string' && (schema.format === 'date' || schema.format === 'date-time')) return 'DateEditor';
8755
9270
 
8756
9271
  return 'StringEditor';
8757
9272
  },
@@ -8859,29 +9374,36 @@ var script = {
8859
9374
  const hasNull = schema.anyOf.some(s => s.type === 'null');
8860
9375
  if (hasNull && nonNull.length === 1) {
8861
9376
  const resolved = this.resolveSchema(nonNull[0]);
8862
- return {
8863
- ...resolved,
8864
- _nullable: true,
8865
- title: schema.title || resolved.title,
8866
- default: 'default' in schema ? schema.default : null,
8867
- };
9377
+ // Carry ALL sibling keys through the nullable collapse: pydantic
9378
+ // emits json_schema_extra (placeholder, minLength/maxLength,
9379
+ // minimum/maximum, format, choice oneOf, ...) as SIBLINGS of
9380
+ // anyOf on Optional fields. Outer keys override the inner branch.
9381
+ const { anyOf, oneOf, ...rest } = schema;
9382
+ const out = { ...resolved, ...rest, _nullable: true };
9383
+ if (!('default' in schema)) out.default = null;
9384
+ if (isChoiceOneOf(oneOf)) out.oneOf = oneOf;
9385
+ return out;
8868
9386
  }
8869
9387
  if (nonNull.length >= 1) return this.resolveSchema(nonNull[0]);
8870
9388
  }
8871
9389
 
8872
9390
  if (schema.oneOf && schema.discriminator) return schema;
8873
9391
 
9392
+ // A oneOf of {const, title} value options is a choice list (select),
9393
+ // not alternative sub-schemas: keep it intact for SelectEditor instead
9394
+ // of collapsing to the first member (which routed to HiddenEditor and
9395
+ // overwrote stored values on mount).
9396
+ if (isChoiceOneOf(schema.oneOf)) return schema;
9397
+
8874
9398
  if (schema.oneOf) {
8875
9399
  const nonNull = schema.oneOf.filter(s => s.type !== 'null');
8876
9400
  const hasNull = schema.oneOf.some(s => s.type === 'null');
8877
9401
  if (hasNull && nonNull.length === 1) {
8878
9402
  const resolved = this.resolveSchema(nonNull[0]);
8879
- return {
8880
- ...resolved,
8881
- _nullable: true,
8882
- title: schema.title || resolved.title,
8883
- default: 'default' in schema ? schema.default : null,
8884
- };
9403
+ const { anyOf, oneOf, ...rest } = schema;
9404
+ const out = { ...resolved, ...rest, _nullable: true };
9405
+ if (!('default' in schema)) out.default = null;
9406
+ return out;
8885
9407
  }
8886
9408
  if (nonNull.length >= 1) return this.resolveSchema(nonNull[0]);
8887
9409
  }
@@ -9085,5 +9607,5 @@ function registerCustomElement(tagName = 'schema-form') {
9085
9607
  }
9086
9608
  }
9087
9609
 
9088
- 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 };
9610
+ export { script$7 as ArrayEditor, BaseEditorElement, script$d as BooleanEditor, script$c as DateEditor, script$a as HiddenEditor, script$6 as NullableEditor, script$e as NumberEditor, script$8 as ObjectEditor, script$4 as RelationEditor, script$1 as SchemaEditor, script as SchemaForm, SchemaFormElement, script$b as SelectEditor, script$f as StringEditor, script$5 as UnionEditor, script$2 as WebComponentWrapper, applyConditionals, hasConditionals, matchesSchema, registerCustomElement };
9089
9611
  //# sourceMappingURL=structured-widget-editor.esm.js.map