@structured-field/widget-editor 1.2.2 → 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$e = {
1
+ var script$f = {
2
2
  name: 'StringEditor',
3
3
  props: {
4
4
  schema: { type: Object, required: true },
@@ -6765,7 +6765,7 @@ class VueElement extends BaseClass {
6765
6765
  _update() {
6766
6766
  const vnode = this._createVNode();
6767
6767
  if (this._app) vnode.appContext = this._app._context;
6768
- render$e(vnode, this._root);
6768
+ render$f(vnode, this._root);
6769
6769
  }
6770
6770
  _createVNode() {
6771
6771
  const baseProps = {};
@@ -7032,7 +7032,7 @@ let renderer;
7032
7032
  function ensureRenderer() {
7033
7033
  return renderer || (renderer = createRenderer(rendererOptions));
7034
7034
  }
7035
- const render$e = ((...args) => {
7035
+ const render$f = ((...args) => {
7036
7036
  ensureRenderer().render(...args);
7037
7037
  });
7038
7038
  const createApp = ((...args) => {
@@ -7073,18 +7073,18 @@ function normalizeContainer(container) {
7073
7073
  return container;
7074
7074
  }
7075
7075
 
7076
- const _hoisted_1$c = {
7076
+ const _hoisted_1$d = {
7077
7077
  key: 0,
7078
7078
  class: "sf-null-badge"
7079
7079
  };
7080
- const _hoisted_2$a = ["value", "placeholder"];
7081
- const _hoisted_3$a = ["value", "placeholder"];
7080
+ const _hoisted_2$b = ["value", "placeholder"];
7081
+ const _hoisted_3$b = ["value", "placeholder"];
7082
7082
  const _hoisted_4$7 = {
7083
7083
  key: 0,
7084
7084
  class: "errorlist"
7085
7085
  };
7086
7086
 
7087
- function render$d(_ctx, _cache, $props, $setup, $data, $options) {
7087
+ function render$e(_ctx, _cache, $props, $setup, $data, $options) {
7088
7088
  return (openBlock(), createElementBlock("div", {
7089
7089
  class: normalizeClass(["sf-field", { errors: $options.fieldErrors.length }])
7090
7090
  }, [
@@ -7093,7 +7093,7 @@ function render$d(_ctx, _cache, $props, $setup, $data, $options) {
7093
7093
  }, [
7094
7094
  createTextVNode(toDisplayString($options.title) + " ", 1 /* TEXT */),
7095
7095
  ($options.isNullable && $options.isNullValue)
7096
- ? (openBlock(), createElementBlock("span", _hoisted_1$c, "null"))
7096
+ ? (openBlock(), createElementBlock("span", _hoisted_1$d, "null"))
7097
7097
  : createCommentVNode("v-if", true)
7098
7098
  ], 2 /* CLASS */),
7099
7099
  createBaseVNode("div", {
@@ -7107,7 +7107,7 @@ function render$d(_ctx, _cache, $props, $setup, $data, $options) {
7107
7107
  value: $options.isNullValue ? '' : $props.modelValue,
7108
7108
  placeholder: $options.isNullValue ? 'null' : ($props.schema.placeholder || ''),
7109
7109
  onInput: _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event.target.value)))
7110
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$a))
7110
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$b))
7111
7111
  : (openBlock(), createElementBlock("input", {
7112
7112
  key: 1,
7113
7113
  type: "text",
@@ -7115,7 +7115,7 @@ function render$d(_ctx, _cache, $props, $setup, $data, $options) {
7115
7115
  value: $options.isNullValue ? '' : ($props.modelValue != null ? String($props.modelValue) : ''),
7116
7116
  placeholder: $options.isNullValue ? 'null' : ($props.schema.placeholder || ''),
7117
7117
  onInput: _cache[1] || (_cache[1] = $event => (_ctx.$emit('update:modelValue', $event.target.value)))
7118
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_3$a)),
7118
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_3$b)),
7119
7119
  ($options.isNullable && !$options.isNullValue)
7120
7120
  ? (openBlock(), createElementBlock("button", {
7121
7121
  key: 2,
@@ -7136,10 +7136,10 @@ function render$d(_ctx, _cache, $props, $setup, $data, $options) {
7136
7136
  ], 2 /* CLASS */))
7137
7137
  }
7138
7138
 
7139
- script$e.render = render$d;
7140
- script$e.__file = "src/editors/StringEditor.vue";
7139
+ script$f.render = render$e;
7140
+ script$f.__file = "src/editors/StringEditor.vue";
7141
7141
 
7142
- var script$d = {
7142
+ var script$e = {
7143
7143
  name: 'NumberEditor',
7144
7144
  props: {
7145
7145
  schema: { type: Object, required: true },
@@ -7186,17 +7186,17 @@ var script$d = {
7186
7186
  },
7187
7187
  };
7188
7188
 
7189
- const _hoisted_1$b = {
7189
+ const _hoisted_1$c = {
7190
7190
  key: 0,
7191
7191
  class: "sf-null-badge"
7192
7192
  };
7193
- const _hoisted_2$9 = ["step", "min", "max", "value", "placeholder"];
7194
- const _hoisted_3$9 = {
7193
+ const _hoisted_2$a = ["step", "min", "max", "value", "placeholder"];
7194
+ const _hoisted_3$a = {
7195
7195
  key: 0,
7196
7196
  class: "errorlist"
7197
7197
  };
7198
7198
 
7199
- function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7199
+ function render$d(_ctx, _cache, $props, $setup, $data, $options) {
7200
7200
  return (openBlock(), createElementBlock("div", {
7201
7201
  class: normalizeClass(["sf-field", { errors: $options.fieldErrors.length }])
7202
7202
  }, [
@@ -7205,7 +7205,7 @@ function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7205
7205
  }, [
7206
7206
  createTextVNode(toDisplayString($options.title) + " ", 1 /* TEXT */),
7207
7207
  ($options.isNullable && $options.isNullValue)
7208
- ? (openBlock(), createElementBlock("span", _hoisted_1$b, "null"))
7208
+ ? (openBlock(), createElementBlock("span", _hoisted_1$c, "null"))
7209
7209
  : createCommentVNode("v-if", true)
7210
7210
  ], 2 /* CLASS */),
7211
7211
  createBaseVNode("div", {
@@ -7220,7 +7220,7 @@ function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7220
7220
  value: $options.isNullValue ? '' : $props.modelValue,
7221
7221
  placeholder: $options.isNullValue ? 'null' : undefined,
7222
7222
  onInput: _cache[0] || (_cache[0] = (...args) => ($options.onInput && $options.onInput(...args)))
7223
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$9),
7223
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$a),
7224
7224
  ($options.isNullable && !$options.isNullValue)
7225
7225
  ? (openBlock(), createElementBlock("button", {
7226
7226
  key: 0,
@@ -7232,7 +7232,7 @@ function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7232
7232
  : createCommentVNode("v-if", true)
7233
7233
  ], 2 /* CLASS */),
7234
7234
  ($options.fieldErrors.length)
7235
- ? (openBlock(), createElementBlock("ul", _hoisted_3$9, [
7235
+ ? (openBlock(), createElementBlock("ul", _hoisted_3$a, [
7236
7236
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.fieldErrors, (err, i) => {
7237
7237
  return (openBlock(), createElementBlock("li", { key: i }, toDisplayString(err), 1 /* TEXT */))
7238
7238
  }), 128 /* KEYED_FRAGMENT */))
@@ -7241,10 +7241,10 @@ function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7241
7241
  ], 2 /* CLASS */))
7242
7242
  }
7243
7243
 
7244
- script$d.render = render$c;
7245
- script$d.__file = "src/editors/NumberEditor.vue";
7244
+ script$e.render = render$d;
7245
+ script$e.__file = "src/editors/NumberEditor.vue";
7246
7246
 
7247
- var script$c = {
7247
+ var script$d = {
7248
7248
  name: 'BooleanEditor',
7249
7249
  props: {
7250
7250
  schema: { type: Object, required: true },
@@ -7276,9 +7276,9 @@ var script$c = {
7276
7276
  },
7277
7277
  };
7278
7278
 
7279
- const _hoisted_1$a = { class: "sf-boolean-row" };
7280
- const _hoisted_2$8 = { class: "sf-checkbox-label" };
7281
- const _hoisted_3$8 = ["checked"];
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
7282
  const _hoisted_4$6 = {
7283
7283
  key: 0,
7284
7284
  class: "sf-null-badge"
@@ -7288,18 +7288,18 @@ const _hoisted_5$6 = {
7288
7288
  class: "errorlist"
7289
7289
  };
7290
7290
 
7291
- function render$b(_ctx, _cache, $props, $setup, $data, $options) {
7291
+ function render$c(_ctx, _cache, $props, $setup, $data, $options) {
7292
7292
  return (openBlock(), createElementBlock("div", {
7293
7293
  class: normalizeClass(["sf-field sf-field-boolean", { errors: $options.fieldErrors.length }])
7294
7294
  }, [
7295
- createBaseVNode("div", _hoisted_1$a, [
7296
- createBaseVNode("label", _hoisted_2$8, [
7295
+ createBaseVNode("div", _hoisted_1$b, [
7296
+ createBaseVNode("label", _hoisted_2$9, [
7297
7297
  createBaseVNode("input", {
7298
7298
  type: "checkbox",
7299
7299
  class: "sf-checkbox",
7300
7300
  checked: !!$props.modelValue,
7301
7301
  onChange: _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event.target.checked)))
7302
- }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_3$8),
7302
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_3$9),
7303
7303
  createTextVNode(" " + toDisplayString($options.title) + " ", 1 /* TEXT */),
7304
7304
  ($options.isNullable && $options.isNullValue)
7305
7305
  ? (openBlock(), createElementBlock("span", _hoisted_4$6, "null"))
@@ -7325,8 +7325,192 @@ function render$b(_ctx, _cache, $props, $setup, $data, $options) {
7325
7325
  ], 2 /* CLASS */))
7326
7326
  }
7327
7327
 
7328
+ script$d.render = render$c;
7329
+ script$d.__file = "src/editors/BooleanEditor.vue";
7330
+
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
+
7328
7455
  script$c.render = render$b;
7329
- script$c.__file = "src/editors/BooleanEditor.vue";
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
+ }
7330
7514
 
7331
7515
  var script$b = {
7332
7516
  name: 'SelectEditor',
@@ -7338,6 +7522,20 @@ var script$b = {
7338
7522
  },
7339
7523
  emits: ['update:modelValue'],
7340
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
+ },
7341
7539
  title() {
7342
7540
  return this.schema.title || this.humanize(this.path[this.path.length - 1]) || '';
7343
7541
  },
@@ -7360,6 +7558,13 @@ var script$b = {
7360
7558
  },
7361
7559
  },
7362
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
+ },
7363
7568
  humanize(str) {
7364
7569
  if (!str) return '';
7365
7570
  return str.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2').replace(/^./, s => s.toUpperCase());
@@ -7401,17 +7606,17 @@ function render$a(_ctx, _cache, $props, $setup, $data, $options) {
7401
7606
  }, [
7402
7607
  createBaseVNode("select", {
7403
7608
  class: "sf-input sf-select",
7404
- value: $options.isNullValue ? '' : ($props.modelValue != null ? String($props.modelValue) : ''),
7405
- onChange: _cache[0] || (_cache[0] = $event => (_ctx.$emit('update:modelValue', $event.target.value)))
7609
+ value: $options.selectedIndex === -1 ? '' : String($options.selectedIndex),
7610
+ onChange: _cache[0] || (_cache[0] = $event => ($options.onChange($event.target.value)))
7406
7611
  }, [
7407
- ($options.isNullable && $options.isNullValue)
7408
- ? (openBlock(), createElementBlock("option", _hoisted_3$7, "null"))
7612
+ ($options.selectedIndex === -1)
7613
+ ? (openBlock(), createElementBlock("option", _hoisted_3$7, toDisplayString($options.isNullValue ? 'null' : ''), 1 /* TEXT */))
7409
7614
  : createCommentVNode("v-if", true),
7410
- (openBlock(true), createElementBlock(Fragment, null, renderList(($props.schema.enum || []), (opt) => {
7615
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.options, (opt, i) => {
7411
7616
  return (openBlock(), createElementBlock("option", {
7412
- key: opt,
7413
- value: String(opt)
7414
- }, toDisplayString(opt), 9 /* TEXT, PROPS */, _hoisted_4$5))
7617
+ key: i,
7618
+ value: String(i)
7619
+ }, toDisplayString(opt.label), 9 /* TEXT, PROPS */, _hoisted_4$5))
7415
7620
  }), 128 /* KEYED_FRAGMENT */))
7416
7621
  ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_2$7),
7417
7622
  ($options.isNullable && !$options.isNullValue)
@@ -7835,6 +8040,9 @@ var script$8 = {
7835
8040
  data() {
7836
8041
  return {
7837
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: {},
7838
8046
  };
7839
8047
  },
7840
8048
  computed: {
@@ -7882,10 +8090,21 @@ var script$8 = {
7882
8090
  const allowed = new Set(Object.keys(effective.properties || {}));
7883
8091
  let changed = false;
7884
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
+ }
7885
8101
  for (const k of Object.keys(value)) {
7886
8102
  if (allowed.has(k)) {
7887
8103
  out[k] = value[k];
7888
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];
7889
8108
  changed = true;
7890
8109
  }
7891
8110
  }
@@ -7968,45 +8187,6 @@ function render$7(_ctx, _cache, $props, $setup, $data, $options) {
7968
8187
  script$8.render = render$7;
7969
8188
  script$8.__file = "src/editors/ObjectEditor.vue";
7970
8189
 
7971
- function debounce(fn, delay) {
7972
- let timer;
7973
- return function (...args) {
7974
- clearTimeout(timer);
7975
- timer = setTimeout(() => fn.apply(this, args), delay);
7976
- };
7977
- }
7978
-
7979
- function deepClone(obj) {
7980
- if (obj === null || typeof obj !== 'object') return obj;
7981
- if (Array.isArray(obj)) return obj.map(deepClone);
7982
- const clone = {};
7983
- for (const key of Object.keys(obj)) {
7984
- clone[key] = deepClone(obj[key]);
7985
- }
7986
- return clone;
7987
- }
7988
-
7989
- function getDefaultForSchema(schema) {
7990
- if ('default' in schema) return deepClone(schema.default);
7991
- if (schema.type === 'object') {
7992
- const obj = {};
7993
- for (const [key, prop] of Object.entries(schema.properties || {})) {
7994
- if ('default' in prop) obj[key] = deepClone(prop.default);
7995
- else if (prop.type === 'string') obj[key] = '';
7996
- else if (prop.type === 'integer' || prop.type === 'number') obj[key] = 0;
7997
- else if (prop.type === 'boolean') obj[key] = false;
7998
- else if (prop.type === 'array') obj[key] = [];
7999
- }
8000
- return obj;
8001
- }
8002
- if (schema.type === 'array') return [];
8003
- if (schema.type === 'string') return '';
8004
- if (schema.type === 'integer' || schema.type === 'number') return 0;
8005
- if (schema.type === 'boolean') return false;
8006
- if (schema.type === 'relation') return schema.multiple ? [] : null;
8007
- return null;
8008
- }
8009
-
8010
8190
  let keyCounter = 0;
8011
8191
 
8012
8192
  var script$7 = {
@@ -9021,9 +9201,10 @@ const MAX_DEPTH = 12;
9021
9201
  var script$1 = {
9022
9202
  name: 'SchemaEditor',
9023
9203
  components: {
9024
- StringEditor: script$e,
9025
- NumberEditor: script$d,
9026
- BooleanEditor: script$c,
9204
+ StringEditor: script$f,
9205
+ NumberEditor: script$e,
9206
+ BooleanEditor: script$d,
9207
+ DateEditor: script$c,
9027
9208
  SelectEditor: script$b,
9028
9209
  HiddenEditor: script$a,
9029
9210
  ObjectEditor: script$8,
@@ -9073,6 +9254,9 @@ var script$1 = {
9073
9254
 
9074
9255
  if (schema.type === 'relation') return 'RelationEditor';
9075
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';
9076
9260
  if ('const' in schema) return 'HiddenEditor';
9077
9261
  if (schema.enum && schema.enum.length === 1 && schema.type === 'string') return 'HiddenEditor';
9078
9262
  if (schema._nullable && (schema.type === 'object' || schema.type === 'array')) return 'NullableEditor';
@@ -9082,6 +9266,7 @@ var script$1 = {
9082
9266
  if (schema.enum) return 'SelectEditor';
9083
9267
  if (schema.type === 'boolean') return 'BooleanEditor';
9084
9268
  if (schema.type === 'number' || schema.type === 'integer') return 'NumberEditor';
9269
+ if (schema.type === 'string' && (schema.format === 'date' || schema.format === 'date-time')) return 'DateEditor';
9085
9270
 
9086
9271
  return 'StringEditor';
9087
9272
  },
@@ -9189,29 +9374,36 @@ var script = {
9189
9374
  const hasNull = schema.anyOf.some(s => s.type === 'null');
9190
9375
  if (hasNull && nonNull.length === 1) {
9191
9376
  const resolved = this.resolveSchema(nonNull[0]);
9192
- return {
9193
- ...resolved,
9194
- _nullable: true,
9195
- title: schema.title || resolved.title,
9196
- default: 'default' in schema ? schema.default : null,
9197
- };
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;
9198
9386
  }
9199
9387
  if (nonNull.length >= 1) return this.resolveSchema(nonNull[0]);
9200
9388
  }
9201
9389
 
9202
9390
  if (schema.oneOf && schema.discriminator) return schema;
9203
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
+
9204
9398
  if (schema.oneOf) {
9205
9399
  const nonNull = schema.oneOf.filter(s => s.type !== 'null');
9206
9400
  const hasNull = schema.oneOf.some(s => s.type === 'null');
9207
9401
  if (hasNull && nonNull.length === 1) {
9208
9402
  const resolved = this.resolveSchema(nonNull[0]);
9209
- return {
9210
- ...resolved,
9211
- _nullable: true,
9212
- title: schema.title || resolved.title,
9213
- default: 'default' in schema ? schema.default : null,
9214
- };
9403
+ const { anyOf, oneOf, ...rest } = schema;
9404
+ const out = { ...resolved, ...rest, _nullable: true };
9405
+ if (!('default' in schema)) out.default = null;
9406
+ return out;
9215
9407
  }
9216
9408
  if (nonNull.length >= 1) return this.resolveSchema(nonNull[0]);
9217
9409
  }
@@ -9415,5 +9607,5 @@ function registerCustomElement(tagName = 'schema-form') {
9415
9607
  }
9416
9608
  }
9417
9609
 
9418
- export { script$7 as ArrayEditor, BaseEditorElement, script$c as BooleanEditor, script$a as HiddenEditor, script$6 as NullableEditor, script$d as NumberEditor, script$8 as ObjectEditor, script$4 as RelationEditor, script$1 as SchemaEditor, script as SchemaForm, SchemaFormElement, script$b as SelectEditor, script$e as StringEditor, script$5 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 };
9419
9611
  //# sourceMappingURL=structured-widget-editor.esm.js.map