@structured-field/widget-editor 1.2.2 → 1.4.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.
- package/README.md +51 -0
- package/dist/structured-widget-editor.css +1 -1
- package/dist/structured-widget-editor.esm.js +470 -125
- package/dist/structured-widget-editor.esm.js.map +1 -1
- package/dist/structured-widget-editor.iife.js +5 -5
- package/dist/structured-widget-editor.js +4 -4
- package/dist/structured-widget-editor.js.map +1 -1
- package/package.json +8 -2
- package/src/SchemaForm.vue +20 -13
- package/src/editors/DateEditor.vue +95 -0
- package/src/editors/ObjectEditor.vue +49 -18
- package/src/editors/RelationEditor.vue +1 -1
- package/src/editors/SchemaEditor.vue +8 -2
- package/src/editors/SelectEditor.vue +28 -5
- package/src/index.js +2 -0
- package/src/layout.js +116 -0
- package/src/scss/components/layout.scss +76 -0
- package/src/scss/main.scss +1 -0
- package/src/utils.js +18 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var script$
|
|
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$
|
|
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$
|
|
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$
|
|
7076
|
+
const _hoisted_1$d = {
|
|
7077
7077
|
key: 0,
|
|
7078
7078
|
class: "sf-null-badge"
|
|
7079
7079
|
};
|
|
7080
|
-
const _hoisted_2$
|
|
7081
|
-
const _hoisted_3$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
7140
|
-
script$
|
|
7139
|
+
script$f.render = render$e;
|
|
7140
|
+
script$f.__file = "src/editors/StringEditor.vue";
|
|
7141
7141
|
|
|
7142
|
-
var script$
|
|
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$
|
|
7189
|
+
const _hoisted_1$c = {
|
|
7190
7190
|
key: 0,
|
|
7191
7191
|
class: "sf-null-badge"
|
|
7192
7192
|
};
|
|
7193
|
-
const _hoisted_2$
|
|
7194
|
-
const _hoisted_3$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
7245
|
-
script$
|
|
7244
|
+
script$e.render = render$d;
|
|
7245
|
+
script$e.__file = "src/editors/NumberEditor.vue";
|
|
7246
7246
|
|
|
7247
|
-
var script$
|
|
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$
|
|
7280
|
-
const _hoisted_2$
|
|
7281
|
-
const _hoisted_3$
|
|
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$
|
|
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$
|
|
7296
|
-
createBaseVNode("label", _hoisted_2$
|
|
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$
|
|
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/
|
|
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.
|
|
7405
|
-
onChange: _cache[0] || (_cache[0] = $event => (
|
|
7609
|
+
value: $options.selectedIndex === -1 ? '' : String($options.selectedIndex),
|
|
7610
|
+
onChange: _cache[0] || (_cache[0] = $event => ($options.onChange($event.target.value)))
|
|
7406
7611
|
}, [
|
|
7407
|
-
($options.
|
|
7408
|
-
? (openBlock(), createElementBlock("option", _hoisted_3$7,
|
|
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(
|
|
7615
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($options.options, (opt, i) => {
|
|
7411
7616
|
return (openBlock(), createElementBlock("option", {
|
|
7412
|
-
key:
|
|
7413
|
-
value: String(
|
|
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)
|
|
@@ -7818,6 +8023,121 @@ function hasConditionals(schema) {
|
|
|
7818
8023
|
return false;
|
|
7819
8024
|
}
|
|
7820
8025
|
|
|
8026
|
+
// Field-size classification for the multicolumn flow layout.
|
|
8027
|
+
// Sizes are flex-basis tokens (see scss/components/layout.scss), not column
|
|
8028
|
+
// counts: columns emerge from how many cells fit the container's width.
|
|
8029
|
+
// Invariant: visual order === DOM order === tab order. Never reorder cells.
|
|
8030
|
+
|
|
8031
|
+
const SIZES = ['xs', 'sm', 'md', 'lg', 'full'];
|
|
8032
|
+
const BREAKS = ['before', 'after', 'both'];
|
|
8033
|
+
|
|
8034
|
+
// Schema-author hint: `layout: 'sm'` or `layout: { size: 'sm', break: 'before' }`.
|
|
8035
|
+
// Invalid hints are silently ignored so a typo degrades to the heuristic.
|
|
8036
|
+
function normalizeLayoutHint(layout) {
|
|
8037
|
+
if (typeof layout === 'string') {
|
|
8038
|
+
return { size: SIZES.includes(layout) ? layout : null, break: null };
|
|
8039
|
+
}
|
|
8040
|
+
if (layout && typeof layout === 'object' && !Array.isArray(layout)) {
|
|
8041
|
+
return {
|
|
8042
|
+
size: SIZES.includes(layout.size) ? layout.size : null,
|
|
8043
|
+
break: BREAKS.includes(layout.break) ? layout.break : null,
|
|
8044
|
+
};
|
|
8045
|
+
}
|
|
8046
|
+
return { size: null, break: null };
|
|
8047
|
+
}
|
|
8048
|
+
|
|
8049
|
+
// Nullable scalars carry the inline null-clear button (~30px of chrome), and
|
|
8050
|
+
// datetime-local needs its full width — one tier of slack keeps them usable.
|
|
8051
|
+
const NULLABLE_BUMP = { xs: 'sm', sm: 'md' };
|
|
8052
|
+
|
|
8053
|
+
function bumpNullable(size, schema) {
|
|
8054
|
+
return schema._nullable ? (NULLABLE_BUMP[size] || size) : size;
|
|
8055
|
+
}
|
|
8056
|
+
|
|
8057
|
+
function choiceSize(labels, schema) {
|
|
8058
|
+
const compact = labels.length <= 8 && labels.every((l) => String(l ?? '').length <= 12);
|
|
8059
|
+
return bumpNullable(compact ? 'sm' : 'md', schema);
|
|
8060
|
+
}
|
|
8061
|
+
|
|
8062
|
+
// Shared with SchemaEditor: past this depth everything renders StringEditor.
|
|
8063
|
+
const MAX_DEPTH = 12;
|
|
8064
|
+
|
|
8065
|
+
// Intrinsic size of a RESOLVED schema node.
|
|
8066
|
+
// Returns 'xs' | 'sm' | 'md' | 'lg' | 'full' | 'hidden'.
|
|
8067
|
+
// Hidden routing (const / single-string-enum) is authoritative: a size hint
|
|
8068
|
+
// on a field that renders HiddenEditor would only produce an empty cell.
|
|
8069
|
+
function fieldSize(schema) {
|
|
8070
|
+
if (!schema || typeof schema !== 'object') return 'full';
|
|
8071
|
+
const intrinsic = intrinsicSize(schema);
|
|
8072
|
+
if (intrinsic === 'hidden') return 'hidden';
|
|
8073
|
+
return normalizeLayoutHint(schema.layout).size || intrinsic;
|
|
8074
|
+
}
|
|
8075
|
+
|
|
8076
|
+
// The branch order mirrors SchemaEditor.editorComponent — keep them in sync.
|
|
8077
|
+
function intrinsicSize(schema) {
|
|
8078
|
+
if (schema.type === 'relation') return schema.multiple ? 'lg' : 'md';
|
|
8079
|
+
if (schema.oneOf && schema.discriminator) return 'full';
|
|
8080
|
+
if (isChoiceOneOf(schema.oneOf)) {
|
|
8081
|
+
return choiceSize(schema.oneOf.map((o) => o.title ?? o.const), schema);
|
|
8082
|
+
}
|
|
8083
|
+
if ('const' in schema) return 'hidden';
|
|
8084
|
+
if (schema.enum && schema.enum.length === 1 && schema.type === 'string') return 'hidden';
|
|
8085
|
+
// Covers ObjectEditor, JsonEditor, ArrayEditor and NullableEditor containers.
|
|
8086
|
+
if (schema.type === 'object' || schema.type === 'array') return 'full';
|
|
8087
|
+
if (schema.enum) return choiceSize(schema.enum, schema);
|
|
8088
|
+
if (schema.type === 'boolean') return bumpNullable('xs', schema);
|
|
8089
|
+
if (schema.type === 'number' || schema.type === 'integer') return bumpNullable('xs', schema);
|
|
8090
|
+
if (schema.type === 'string') {
|
|
8091
|
+
if (schema.format === 'date') return bumpNullable('sm', schema);
|
|
8092
|
+
if (schema.format === 'date-time') return bumpNullable('md', schema);
|
|
8093
|
+
// Mirrors StringEditor.isLong (textarea rendering).
|
|
8094
|
+
if (schema.format === 'textarea' || schema.maxLength > 255) return 'full';
|
|
8095
|
+
if (schema.maxLength > 0 && schema.maxLength <= 40) return bumpNullable('sm', schema);
|
|
8096
|
+
return 'md';
|
|
8097
|
+
}
|
|
8098
|
+
return 'full';
|
|
8099
|
+
}
|
|
8100
|
+
|
|
8101
|
+
// Builds the cell list for an object's properties: resolved schema, wrapper
|
|
8102
|
+
// classes and row-break flags. `break: 'after'` marks the NEXT visible field;
|
|
8103
|
+
// hidden fields neither consume nor emit a pending break.
|
|
8104
|
+
// Custom-editor matches default to 'full' (we can't predict their rendering)
|
|
8105
|
+
// unless the schema hint or the override's own `layout` says otherwise.
|
|
8106
|
+
function layoutCells(properties, { resolveSchema, customEditors = [], basePath = [] } = {}) {
|
|
8107
|
+
const cells = [];
|
|
8108
|
+
let pendingBreak = false;
|
|
8109
|
+
// Mirrors SchemaEditor: the depth guard runs before custom-editor overrides,
|
|
8110
|
+
// and past it every field (const included) renders a visible StringEditor.
|
|
8111
|
+
const pastMaxDepth = basePath.length + 1 > MAX_DEPTH;
|
|
8112
|
+
for (const [key, raw] of Object.entries(properties || {})) {
|
|
8113
|
+
const schema = resolveSchema ? resolveSchema(raw) : raw;
|
|
8114
|
+
const hint = normalizeLayoutHint(schema.layout);
|
|
8115
|
+
const override = !pastMaxDepth
|
|
8116
|
+
&& customEditors.find((o) => o.match && o.match(schema, [...basePath, key]));
|
|
8117
|
+
let size;
|
|
8118
|
+
if (pastMaxDepth) {
|
|
8119
|
+
size = 'md';
|
|
8120
|
+
} else if (override) {
|
|
8121
|
+
size = hint.size || normalizeLayoutHint(override.layout).size || 'full';
|
|
8122
|
+
} else {
|
|
8123
|
+
size = fieldSize(schema);
|
|
8124
|
+
}
|
|
8125
|
+
const classes = ['sf-cell', `sf-cell-${size}`];
|
|
8126
|
+
// Only the plain-boolean shape reaches the label-less BooleanEditor;
|
|
8127
|
+
// boolean enums / choice oneOfs render SelectEditor (which has a label).
|
|
8128
|
+
const isCheckbox = schema.type === 'boolean' && !override && !pastMaxDepth
|
|
8129
|
+
&& !schema.enum && !schema.oneOf && !('const' in schema);
|
|
8130
|
+
let breakBefore = false;
|
|
8131
|
+
if (size !== 'hidden') {
|
|
8132
|
+
if (isCheckbox) classes.push('sf-cell-bool');
|
|
8133
|
+
breakBefore = pendingBreak || hint.break === 'before' || hint.break === 'both';
|
|
8134
|
+
pendingBreak = hint.break === 'after' || hint.break === 'both';
|
|
8135
|
+
}
|
|
8136
|
+
cells.push({ key, schema, classes: classes.join(' '), breakBefore });
|
|
8137
|
+
}
|
|
8138
|
+
return cells;
|
|
8139
|
+
}
|
|
8140
|
+
|
|
7821
8141
|
var script$8 = {
|
|
7822
8142
|
name: 'ObjectEditor',
|
|
7823
8143
|
beforeCreate() {
|
|
@@ -7825,6 +8145,9 @@ var script$8 = {
|
|
|
7825
8145
|
this.$options.components.SchemaEditor = script$1;
|
|
7826
8146
|
this.$options.components.SfIcon = script$9;
|
|
7827
8147
|
},
|
|
8148
|
+
inject: {
|
|
8149
|
+
customEditors: { default: () => () => [] },
|
|
8150
|
+
},
|
|
7828
8151
|
props: {
|
|
7829
8152
|
schema: { type: Object, required: true },
|
|
7830
8153
|
modelValue: { default: () => ({}) },
|
|
@@ -7835,6 +8158,9 @@ var script$8 = {
|
|
|
7835
8158
|
data() {
|
|
7836
8159
|
return {
|
|
7837
8160
|
collapsed: false,
|
|
8161
|
+
// Values pruned when a conditional rule deactivated their field,
|
|
8162
|
+
// kept so toggling the controller back restores what the user typed.
|
|
8163
|
+
prunedStash: {},
|
|
7838
8164
|
};
|
|
7839
8165
|
},
|
|
7840
8166
|
computed: {
|
|
@@ -7848,6 +8174,13 @@ var script$8 = {
|
|
|
7848
8174
|
if (!hasConditionals(this.schema)) return this.schema;
|
|
7849
8175
|
return applyConditionals(this.schema, this.modelValue || {}, this.form?.resolveSchema);
|
|
7850
8176
|
},
|
|
8177
|
+
cells() {
|
|
8178
|
+
return layoutCells(this.effectiveSchema.properties, {
|
|
8179
|
+
resolveSchema: this.form?.resolveSchema,
|
|
8180
|
+
customEditors: this.customEditors(),
|
|
8181
|
+
basePath: this.path,
|
|
8182
|
+
});
|
|
8183
|
+
},
|
|
7851
8184
|
summary() {
|
|
7852
8185
|
const val = this.modelValue || {};
|
|
7853
8186
|
const parts = [];
|
|
@@ -7882,10 +8215,21 @@ var script$8 = {
|
|
|
7882
8215
|
const allowed = new Set(Object.keys(effective.properties || {}));
|
|
7883
8216
|
let changed = false;
|
|
7884
8217
|
const out = {};
|
|
8218
|
+
// restore stashed values for fields a rule just re-activated
|
|
8219
|
+
for (const k of allowed) {
|
|
8220
|
+
if (!(k in value) && k in this.prunedStash) {
|
|
8221
|
+
out[k] = this.prunedStash[k];
|
|
8222
|
+
delete this.prunedStash[k];
|
|
8223
|
+
changed = true;
|
|
8224
|
+
}
|
|
8225
|
+
}
|
|
7885
8226
|
for (const k of Object.keys(value)) {
|
|
7886
8227
|
if (allowed.has(k)) {
|
|
7887
8228
|
out[k] = value[k];
|
|
7888
8229
|
} else {
|
|
8230
|
+
// pruned from the emitted value (documented behavior), but kept
|
|
8231
|
+
// locally so a controller toggle round-trip is not destructive
|
|
8232
|
+
this.prunedStash[k] = value[k];
|
|
7889
8233
|
changed = true;
|
|
7890
8234
|
}
|
|
7891
8235
|
}
|
|
@@ -7899,14 +8243,24 @@ const _hoisted_1$6 = {
|
|
|
7899
8243
|
class: "sf-object sf-object-root"
|
|
7900
8244
|
};
|
|
7901
8245
|
const _hoisted_2$5 = { class: "sf-object-fields" };
|
|
7902
|
-
const _hoisted_3$5 = {
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
8246
|
+
const _hoisted_3$5 = {
|
|
8247
|
+
key: 0,
|
|
8248
|
+
class: "sf-flow-break",
|
|
8249
|
+
"aria-hidden": "true"
|
|
8250
|
+
};
|
|
8251
|
+
const _hoisted_4$4 = { class: "sf-object-title" };
|
|
8252
|
+
const _hoisted_5$4 = ["aria-label"];
|
|
8253
|
+
const _hoisted_6$3 = { class: "sf-object-title-text" };
|
|
8254
|
+
const _hoisted_7$2 = {
|
|
7906
8255
|
key: 0,
|
|
7907
8256
|
class: "sf-object-summary"
|
|
7908
8257
|
};
|
|
7909
|
-
const
|
|
8258
|
+
const _hoisted_8$2 = { class: "sf-object-fields" };
|
|
8259
|
+
const _hoisted_9$2 = {
|
|
8260
|
+
key: 0,
|
|
8261
|
+
class: "sf-flow-break",
|
|
8262
|
+
"aria-hidden": "true"
|
|
8263
|
+
};
|
|
7910
8264
|
|
|
7911
8265
|
function render$7(_ctx, _cache, $props, $setup, $data, $options) {
|
|
7912
8266
|
const _component_SchemaEditor = resolveComponent("SchemaEditor");
|
|
@@ -7915,15 +8269,25 @@ function render$7(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
7915
8269
|
return ($options.isRoot)
|
|
7916
8270
|
? (openBlock(), createElementBlock("div", _hoisted_1$6, [
|
|
7917
8271
|
createBaseVNode("div", _hoisted_2$5, [
|
|
7918
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(
|
|
7919
|
-
return (openBlock(),
|
|
7920
|
-
key: key
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
"
|
|
7926
|
-
|
|
8272
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($options.cells, (cell) => {
|
|
8273
|
+
return (openBlock(), createElementBlock(Fragment, {
|
|
8274
|
+
key: cell.key
|
|
8275
|
+
}, [
|
|
8276
|
+
(cell.breakBefore)
|
|
8277
|
+
? (openBlock(), createElementBlock("div", _hoisted_3$5))
|
|
8278
|
+
: createCommentVNode("v-if", true),
|
|
8279
|
+
createBaseVNode("div", {
|
|
8280
|
+
class: normalizeClass(cell.classes)
|
|
8281
|
+
}, [
|
|
8282
|
+
createVNode(_component_SchemaEditor, {
|
|
8283
|
+
schema: cell.schema,
|
|
8284
|
+
"model-value": ($props.modelValue || {})[cell.key],
|
|
8285
|
+
path: [...$props.path, cell.key],
|
|
8286
|
+
form: $props.form,
|
|
8287
|
+
"onUpdate:modelValue": $event => ($options.onChildChange(cell.key, $event))
|
|
8288
|
+
}, null, 8 /* PROPS */, ["schema", "model-value", "path", "form", "onUpdate:modelValue"])
|
|
8289
|
+
], 2 /* CLASS */)
|
|
8290
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
7927
8291
|
}), 128 /* KEYED_FRAGMENT */))
|
|
7928
8292
|
])
|
|
7929
8293
|
]))
|
|
@@ -7931,7 +8295,7 @@ function render$7(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
7931
8295
|
key: 1,
|
|
7932
8296
|
class: normalizeClass(["sf-object", { 'sf-object-collapsed': $data.collapsed }])
|
|
7933
8297
|
}, [
|
|
7934
|
-
createBaseVNode("legend",
|
|
8298
|
+
createBaseVNode("legend", _hoisted_4$4, [
|
|
7935
8299
|
createBaseVNode("button", {
|
|
7936
8300
|
type: "button",
|
|
7937
8301
|
class: "sf-collapse-btn",
|
|
@@ -7942,22 +8306,32 @@ function render$7(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
7942
8306
|
name: $data.collapsed ? 'chevron-down' : 'chevron-up',
|
|
7943
8307
|
size: 12
|
|
7944
8308
|
}, null, 8 /* PROPS */, ["name"])
|
|
7945
|
-
], 8 /* PROPS */,
|
|
7946
|
-
createBaseVNode("span",
|
|
8309
|
+
], 8 /* PROPS */, _hoisted_5$4),
|
|
8310
|
+
createBaseVNode("span", _hoisted_6$3, toDisplayString($options.title), 1 /* TEXT */),
|
|
7947
8311
|
($data.collapsed && $options.summary)
|
|
7948
|
-
? (openBlock(), createElementBlock("span",
|
|
8312
|
+
? (openBlock(), createElementBlock("span", _hoisted_7$2, toDisplayString($options.summary), 1 /* TEXT */))
|
|
7949
8313
|
: createCommentVNode("v-if", true)
|
|
7950
8314
|
]),
|
|
7951
|
-
withDirectives(createBaseVNode("div",
|
|
7952
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(
|
|
7953
|
-
return (openBlock(),
|
|
7954
|
-
key: key
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
"
|
|
7960
|
-
|
|
8315
|
+
withDirectives(createBaseVNode("div", _hoisted_8$2, [
|
|
8316
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($options.cells, (cell) => {
|
|
8317
|
+
return (openBlock(), createElementBlock(Fragment, {
|
|
8318
|
+
key: cell.key
|
|
8319
|
+
}, [
|
|
8320
|
+
(cell.breakBefore)
|
|
8321
|
+
? (openBlock(), createElementBlock("div", _hoisted_9$2))
|
|
8322
|
+
: createCommentVNode("v-if", true),
|
|
8323
|
+
createBaseVNode("div", {
|
|
8324
|
+
class: normalizeClass(cell.classes)
|
|
8325
|
+
}, [
|
|
8326
|
+
createVNode(_component_SchemaEditor, {
|
|
8327
|
+
schema: cell.schema,
|
|
8328
|
+
"model-value": ($props.modelValue || {})[cell.key],
|
|
8329
|
+
path: [...$props.path, cell.key],
|
|
8330
|
+
form: $props.form,
|
|
8331
|
+
"onUpdate:modelValue": $event => ($options.onChildChange(cell.key, $event))
|
|
8332
|
+
}, null, 8 /* PROPS */, ["schema", "model-value", "path", "form", "onUpdate:modelValue"])
|
|
8333
|
+
], 2 /* CLASS */)
|
|
8334
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
7961
8335
|
}), 128 /* KEYED_FRAGMENT */))
|
|
7962
8336
|
], 512 /* NEED_PATCH */), [
|
|
7963
8337
|
[vShow, !$data.collapsed]
|
|
@@ -7968,45 +8342,6 @@ function render$7(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
7968
8342
|
script$8.render = render$7;
|
|
7969
8343
|
script$8.__file = "src/editors/ObjectEditor.vue";
|
|
7970
8344
|
|
|
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
8345
|
let keyCounter = 0;
|
|
8011
8346
|
|
|
8012
8347
|
var script$7 = {
|
|
@@ -8669,7 +9004,7 @@ function render$3(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
8669
9004
|
const _component_SfIcon = resolveComponent("SfIcon");
|
|
8670
9005
|
|
|
8671
9006
|
return (openBlock(), createElementBlock("div", {
|
|
8672
|
-
class: normalizeClass(["sf-field sf-relation", { errors: $options.fieldErrors.length }]),
|
|
9007
|
+
class: normalizeClass(["sf-field sf-relation", { errors: $options.fieldErrors.length, 'sf-relation-multiple': $data.isMultiple, 'sf-relation-open': $data.dropdownVisible }]),
|
|
8673
9008
|
ref: "root"
|
|
8674
9009
|
}, [
|
|
8675
9010
|
createBaseVNode("span", {
|
|
@@ -9016,14 +9351,13 @@ var script$2 = {
|
|
|
9016
9351
|
|
|
9017
9352
|
script$2.__file = "src/editors/WebComponentWrapper.vue";
|
|
9018
9353
|
|
|
9019
|
-
const MAX_DEPTH = 12;
|
|
9020
|
-
|
|
9021
9354
|
var script$1 = {
|
|
9022
9355
|
name: 'SchemaEditor',
|
|
9023
9356
|
components: {
|
|
9024
|
-
StringEditor: script$
|
|
9025
|
-
NumberEditor: script$
|
|
9026
|
-
BooleanEditor: script$
|
|
9357
|
+
StringEditor: script$f,
|
|
9358
|
+
NumberEditor: script$e,
|
|
9359
|
+
BooleanEditor: script$d,
|
|
9360
|
+
DateEditor: script$c,
|
|
9027
9361
|
SelectEditor: script$b,
|
|
9028
9362
|
HiddenEditor: script$a,
|
|
9029
9363
|
ObjectEditor: script$8,
|
|
@@ -9073,6 +9407,9 @@ var script$1 = {
|
|
|
9073
9407
|
|
|
9074
9408
|
if (schema.type === 'relation') return 'RelationEditor';
|
|
9075
9409
|
if (schema.oneOf && schema.discriminator) return 'UnionEditor';
|
|
9410
|
+
// Choice-list oneOf ({const, title} options) renders as a select —
|
|
9411
|
+
// must be checked before the 'const' HiddenEditor routing.
|
|
9412
|
+
if (isChoiceOneOf(schema.oneOf)) return 'SelectEditor';
|
|
9076
9413
|
if ('const' in schema) return 'HiddenEditor';
|
|
9077
9414
|
if (schema.enum && schema.enum.length === 1 && schema.type === 'string') return 'HiddenEditor';
|
|
9078
9415
|
if (schema._nullable && (schema.type === 'object' || schema.type === 'array')) return 'NullableEditor';
|
|
@@ -9082,6 +9419,7 @@ var script$1 = {
|
|
|
9082
9419
|
if (schema.enum) return 'SelectEditor';
|
|
9083
9420
|
if (schema.type === 'boolean') return 'BooleanEditor';
|
|
9084
9421
|
if (schema.type === 'number' || schema.type === 'integer') return 'NumberEditor';
|
|
9422
|
+
if (schema.type === 'string' && (schema.format === 'date' || schema.format === 'date-time')) return 'DateEditor';
|
|
9085
9423
|
|
|
9086
9424
|
return 'StringEditor';
|
|
9087
9425
|
},
|
|
@@ -9189,29 +9527,36 @@ var script = {
|
|
|
9189
9527
|
const hasNull = schema.anyOf.some(s => s.type === 'null');
|
|
9190
9528
|
if (hasNull && nonNull.length === 1) {
|
|
9191
9529
|
const resolved = this.resolveSchema(nonNull[0]);
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
};
|
|
9530
|
+
// Carry ALL sibling keys through the nullable collapse: pydantic
|
|
9531
|
+
// emits json_schema_extra (placeholder, minLength/maxLength,
|
|
9532
|
+
// minimum/maximum, format, choice oneOf, ...) as SIBLINGS of
|
|
9533
|
+
// anyOf on Optional fields. Outer keys override the inner branch.
|
|
9534
|
+
const { anyOf, oneOf, ...rest } = schema;
|
|
9535
|
+
const out = { ...resolved, ...rest, _nullable: true };
|
|
9536
|
+
if (!('default' in schema)) out.default = null;
|
|
9537
|
+
if (isChoiceOneOf(oneOf)) out.oneOf = oneOf;
|
|
9538
|
+
return out;
|
|
9198
9539
|
}
|
|
9199
9540
|
if (nonNull.length >= 1) return this.resolveSchema(nonNull[0]);
|
|
9200
9541
|
}
|
|
9201
9542
|
|
|
9202
9543
|
if (schema.oneOf && schema.discriminator) return schema;
|
|
9203
9544
|
|
|
9545
|
+
// A oneOf of {const, title} value options is a choice list (select),
|
|
9546
|
+
// not alternative sub-schemas: keep it intact for SelectEditor instead
|
|
9547
|
+
// of collapsing to the first member (which routed to HiddenEditor and
|
|
9548
|
+
// overwrote stored values on mount).
|
|
9549
|
+
if (isChoiceOneOf(schema.oneOf)) return schema;
|
|
9550
|
+
|
|
9204
9551
|
if (schema.oneOf) {
|
|
9205
9552
|
const nonNull = schema.oneOf.filter(s => s.type !== 'null');
|
|
9206
9553
|
const hasNull = schema.oneOf.some(s => s.type === 'null');
|
|
9207
9554
|
if (hasNull && nonNull.length === 1) {
|
|
9208
9555
|
const resolved = this.resolveSchema(nonNull[0]);
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
default: 'default' in schema ? schema.default : null,
|
|
9214
|
-
};
|
|
9556
|
+
const { anyOf, oneOf, ...rest } = schema;
|
|
9557
|
+
const out = { ...resolved, ...rest, _nullable: true };
|
|
9558
|
+
if (!('default' in schema)) out.default = null;
|
|
9559
|
+
return out;
|
|
9215
9560
|
}
|
|
9216
9561
|
if (nonNull.length >= 1) return this.resolveSchema(nonNull[0]);
|
|
9217
9562
|
}
|
|
@@ -9415,5 +9760,5 @@ function registerCustomElement(tagName = 'schema-form') {
|
|
|
9415
9760
|
}
|
|
9416
9761
|
}
|
|
9417
9762
|
|
|
9418
|
-
export { script$7 as ArrayEditor, BaseEditorElement, script$
|
|
9763
|
+
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, fieldSize, hasConditionals, layoutCells, matchesSchema, normalizeLayoutHint, registerCustomElement };
|
|
9419
9764
|
//# sourceMappingURL=structured-widget-editor.esm.js.map
|