@ghentcdh/json-forms-vue 1.1.0 → 2.0.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/composables/useFormEvents.d.ts +34 -0
- package/composables/useHttpClient.d.ts +12 -0
- package/forms/Dispatch.vue.d.ts +10 -0
- package/forms/FormComponent.properties.d.ts +64 -0
- package/forms/FormComponent.vue.d.ts +95 -0
- package/forms/errorMessages.d.ts +10 -0
- package/forms/errorMode.d.ts +5 -0
- package/forms/modal/FormModal.properties.d.ts +75 -0
- package/forms/modal/FormModal.vue.d.ts +353 -0
- package/forms/modal/FormModalService.d.ts +42 -0
- package/forms/renderers/array/ArrayRenderer.vue.d.ts +7 -0
- package/forms/renderers/array/ArrayRenderers.d.ts +10 -0
- package/forms/renderers/controls/AutocompleteControlRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/BooleanControlRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/MarkdownControlRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/MultiSelectControlRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/NumberControlRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/SelectControlRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/StringControlRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/TextAreaControlRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/composables/useControlBinding.d.ts +36 -0
- package/forms/renderers/controls/composables/useFetchOption.d.ts +17 -0
- package/forms/renderers/controls/composables/useInput.d.ts +24 -0
- package/forms/renderers/controls/composables/useReadonlyBinding.d.ts +135 -0
- package/forms/renderers/controls/composables/useSelectBinding.d.ts +29 -0
- package/forms/renderers/controls/index.d.ts +10 -0
- package/forms/renderers/controls/readonly/ControlReadonlyRenderer.vue.d.ts +7 -0
- package/forms/renderers/controls/readonly/ReadonlyLabel.vue.d.ts +254 -0
- package/forms/renderers/controls/readonly/__tests__/formatDate.spec.d.ts +1 -0
- package/forms/renderers/controls/readonly/constants.d.ts +2 -0
- package/forms/renderers/controls/readonly/displayValue/BooleanValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/DateValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/LinkValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/MarkdownValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/NotSetValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/NumberValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/ObjectValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/StringValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/ViewDetailValue.vue.d.ts +46 -0
- package/forms/renderers/controls/readonly/displayValue/displayValue.properties.d.ts +23 -0
- package/forms/renderers/controls/readonly/displayValue/formatDate.d.ts +25 -0
- package/forms/renderers/controls/readonly/displayValue/index.d.ts +1 -0
- package/forms/renderers/controls/readonly/index.d.ts +10 -0
- package/forms/renderers/controls/readonly/useDisplayValue.d.ts +1 -0
- package/forms/renderers/controls/resource.d.ts +262 -0
- package/forms/renderers/index.d.ts +43 -0
- package/forms/renderers/layout/CollapseLayoutRenderer.vue.d.ts +7 -0
- package/forms/renderers/layout/LayoutRenderer.vue.d.ts +7 -0
- package/forms/renderers/layout/LayoutRenderers.d.ts +20 -0
- package/forms/renderers/layout/ReadOnlyLayoutRenderer.vue.d.ts +7 -0
- package/forms/renderers/layout/colspan.d.ts +1 -0
- package/forms/renderers/registry.d.ts +7 -0
- package/forms/scope.d.ts +4 -0
- package/forms/types.d.ts +36 -0
- package/http-client.d.ts +14 -0
- package/index.d.ts +15 -8
- package/index.js +1702 -1077
- package/package.json +10 -18
- package/repository/CrudRepository.d.ts +34 -0
- package/repository/index.d.ts +1 -0
- package/table/TableComponent.properties.d.ts +49 -0
- package/table/TableComponent.vue.d.ts +92 -0
- package/table/TableToolbar.vue.d.ts +36 -0
- package/table/cells/index.d.ts +10 -0
- package/table/filter/FilterRowInput.vue.d.ts +17 -0
- package/table/filter/TableFilter.vue.d.ts +16 -0
- package/table/index.d.ts +6 -0
- package/testers/__tests__/jsonforms-testers.spec.d.ts +1 -0
- package/testers/__tests__/tester.spec.d.ts +1 -0
- package/testers/jsonforms-testers.d.ts +27 -0
- package/testers/tester.d.ts +16 -0
- package/view/modal/ViewModal.properties.d.ts +82 -0
- package/view/modal/ViewModal.vue.d.ts +357 -0
- package/form.store.d.ts +0 -8
- package/index.mjs +0 -1892
- package/testing.mjs +0 -1
package/index.js
CHANGED
|
@@ -1,399 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const vue = require("vue");
|
|
4
|
-
const lodashEs = require("lodash-es");
|
|
5
|
-
const jsonFormsCore = require("@ghentcdh/json-forms-core");
|
|
6
4
|
const ui = require("@ghentcdh/ui");
|
|
7
|
-
const core = require("@vueuse/core");
|
|
8
|
-
const vueRouter = require("vue-router");
|
|
9
|
-
const toolsVue = require("@ghentcdh/tools-vue");
|
|
10
|
-
const zod = require("zod");
|
|
11
5
|
const veeValidate = require("vee-validate");
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/** Label for the cancel button. */
|
|
21
|
-
cancelLabel: { type: String, default: "cancel" },
|
|
22
|
-
/** JSON schema describing the shape of the form data. */
|
|
23
|
-
schema: { type: Object, required: true },
|
|
24
|
-
/** UI schema describing the layout and controls. */
|
|
25
|
-
uiSchema: { type: Object, required: true },
|
|
26
|
-
/** Modal width (`'xs'`, `'sm'`, `'md'`, `'lg'`, `'xl'`). */
|
|
27
|
-
modalSize: { type: String, default: "md" },
|
|
28
|
-
/** Callback invoked when the modal closes (with result or `null` on cancel). */
|
|
29
|
-
onClose: {
|
|
30
|
-
type: Function,
|
|
31
|
-
required: true
|
|
32
|
-
},
|
|
33
|
-
/** Callback for form events dispatched by custom renderers. */
|
|
34
|
-
onEvents: {
|
|
35
|
-
type: Function
|
|
36
|
-
},
|
|
37
|
-
/** Initial form data to populate the form with. */
|
|
38
|
-
data: { type: Object, required: true },
|
|
39
|
-
/** When validation errors are shown. */
|
|
40
|
-
errorMode: {
|
|
41
|
-
type: String,
|
|
42
|
-
default: "onBlur"
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const FormModalEmits = [
|
|
46
|
-
/** Emitted when the modal is closed (submit or cancel). */
|
|
47
|
-
"closeModal",
|
|
48
|
-
/** Emitted when a custom renderer dispatches a form event. */
|
|
49
|
-
"events",
|
|
50
|
-
/** Emitted when validation errors change. */
|
|
51
|
-
"errors",
|
|
52
|
-
/** Emitted when form validity changes. */
|
|
53
|
-
"valid"
|
|
54
|
-
];
|
|
55
|
-
const _hoisted_1$8 = { class: "" };
|
|
56
|
-
const _hoisted_2$2 = { class: "flex gap-2 items-center mb-2" };
|
|
57
|
-
const _hoisted_3$1 = { class: "flex gap-2" };
|
|
58
|
-
const _sfc_main$h = /* @__PURE__ */ vue.defineComponent({
|
|
59
|
-
__name: "table-filter",
|
|
60
|
-
props: {
|
|
61
|
-
layout: {},
|
|
62
|
-
filters: {}
|
|
63
|
-
},
|
|
64
|
-
emits: ["changeFilters", "removeFilter"],
|
|
65
|
-
setup(__props, { emit: __emit }) {
|
|
66
|
-
const formData = vue.ref();
|
|
67
|
-
const properties = __props;
|
|
68
|
-
const emits = __emit;
|
|
69
|
-
vue.watch(
|
|
70
|
-
() => properties.filters,
|
|
71
|
-
() => {
|
|
72
|
-
formData.value = {};
|
|
73
|
-
properties.filters.forEach((filter) => {
|
|
74
|
-
formData.value[filter.key] = filter.value;
|
|
75
|
-
});
|
|
76
|
-
},
|
|
77
|
-
{ immediate: true }
|
|
78
|
-
);
|
|
79
|
-
const onResetFilters = () => {
|
|
80
|
-
emits("changeFilters", {});
|
|
81
|
-
};
|
|
82
|
-
const removeFilter = (filter) => {
|
|
83
|
-
formData.value[filter.key] = void 0;
|
|
84
|
-
emits("changeFilters", formData.value);
|
|
85
|
-
};
|
|
6
|
+
const zod = require("zod");
|
|
7
|
+
const jsonFormsCore = require("@ghentcdh/json-forms-core");
|
|
8
|
+
const _hoisted_1$k = { class: "text-xs bg-base-200 p-2 h-full border-b border-gray-200 max-w-96 last-of-type:border-b-0 text-left font-semibold text-text-accent flex items-center" };
|
|
9
|
+
const _hoisted_2$8 = { class: "h-full p-2 whitespace-pre-wrap break-words overflow-hidden min-w-0 text-sm" };
|
|
10
|
+
const _sfc_main$u = /* @__PURE__ */ vue.defineComponent({
|
|
11
|
+
__name: "ReadonlyLabel",
|
|
12
|
+
props: ui.ControlWrapperProperties,
|
|
13
|
+
setup(__props) {
|
|
86
14
|
return (_ctx, _cache) => {
|
|
87
|
-
return vue.openBlock(), vue.createElementBlock(
|
|
88
|
-
vue.createElementVNode("div",
|
|
89
|
-
|
|
90
|
-
key: 0,
|
|
91
|
-
size: "xs",
|
|
92
|
-
outline: true,
|
|
93
|
-
onClick: onResetFilters
|
|
94
|
-
}, {
|
|
95
|
-
default: vue.withCtx(() => [..._cache[0] || (_cache[0] = [
|
|
96
|
-
vue.createTextVNode(" Reset all filters ", -1)
|
|
97
|
-
])]),
|
|
98
|
-
_: 1
|
|
99
|
-
})) : vue.createCommentVNode("", true)
|
|
15
|
+
return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
|
|
16
|
+
vue.createElementVNode("div", _hoisted_1$k, [
|
|
17
|
+
vue.createElementVNode("span", null, vue.toDisplayString(_ctx.label), 1)
|
|
100
18
|
]),
|
|
101
|
-
vue.createElementVNode("div",
|
|
102
|
-
|
|
103
|
-
return vue.openBlock(), vue.createBlock(vue.unref(ui.BtnBadge), {
|
|
104
|
-
key: filter.key,
|
|
105
|
-
icon: vue.unref(ui.IconEnum).Close,
|
|
106
|
-
onClick: ($event) => removeFilter(filter)
|
|
107
|
-
}, {
|
|
108
|
-
default: vue.withCtx(() => [
|
|
109
|
-
vue.createTextVNode(vue.toDisplayString(filter.label) + ": " + vue.toDisplayString(filter.value), 1)
|
|
110
|
-
]),
|
|
111
|
-
_: 2
|
|
112
|
-
}, 1032, ["icon", "onClick"]);
|
|
113
|
-
}), 128))
|
|
114
|
-
])
|
|
115
|
-
]);
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
const TableComponentProperties = {
|
|
120
|
-
id: { type: String, required: true },
|
|
121
|
-
uiSchema: { type: Object, required: true },
|
|
122
|
-
schema: { type: Object, required: true },
|
|
123
|
-
filterUiSchema: { type: Object },
|
|
124
|
-
filterSchema: { type: Object },
|
|
125
|
-
uri: { type: String, required: true },
|
|
126
|
-
reload: { type: Number },
|
|
127
|
-
actions: { type: Array }
|
|
128
|
-
};
|
|
129
|
-
const TableComponentEmits = ["delete", "edit"];
|
|
130
|
-
class TableStore {
|
|
131
|
-
constructor() {
|
|
132
|
-
this.route = vueRouter.useRoute();
|
|
133
|
-
this.router = vueRouter.useRouter();
|
|
134
|
-
this.requestData = vue.ref(jsonFormsCore.RequestSchema.parse(this.route.query));
|
|
135
|
-
this._reload = vue.ref(Date.now());
|
|
136
|
-
this.loading = vue.ref(true);
|
|
137
|
-
this.uri = vue.ref("");
|
|
138
|
-
this.data = core.computedAsync(async () => {
|
|
139
|
-
this._reload.value;
|
|
140
|
-
if (!this.uri.value) return null;
|
|
141
|
-
this.loading.value = true;
|
|
142
|
-
if (this.requestData.value.page < 1) {
|
|
143
|
-
this.requestData.value.page = 1;
|
|
144
|
-
}
|
|
145
|
-
const response = await toolsVue.useApi().get(this.uri.value, {
|
|
146
|
-
params: this.requestData.value
|
|
147
|
-
}).catch((error) => {
|
|
148
|
-
console.error(error);
|
|
149
|
-
return { data: [], request: { totalPages: 1, page: 1 } };
|
|
150
|
-
}).finally(() => this.loading.value = false);
|
|
151
|
-
const data = response.data;
|
|
152
|
-
if (data.request.totalPages < data.request.page) {
|
|
153
|
-
this.updateRequest({ page: data.request.totalPages });
|
|
154
|
-
}
|
|
155
|
-
return data;
|
|
156
|
-
});
|
|
157
|
-
this.pageData = vue.computed(() => {
|
|
158
|
-
const request = this.data.value?.request ?? {
|
|
159
|
-
count: 0,
|
|
160
|
-
pageSize: 1,
|
|
161
|
-
page: 1
|
|
162
|
-
};
|
|
163
|
-
return {
|
|
164
|
-
count: request.count,
|
|
165
|
-
pageSize: request.pageSize,
|
|
166
|
-
page: request.page
|
|
167
|
-
};
|
|
168
|
-
});
|
|
169
|
-
this.tableData = vue.computed(() => {
|
|
170
|
-
const d = this.data.value;
|
|
171
|
-
if (!d) return [];
|
|
172
|
-
if (this.loading.value) return [];
|
|
173
|
-
return d.data ?? [];
|
|
174
|
-
});
|
|
175
|
-
this.init = (url) => {
|
|
176
|
-
this.uri.value = url;
|
|
177
|
-
};
|
|
178
|
-
this.updateRequest = (data) => {
|
|
179
|
-
this.requestData.value = { ...this.requestData.value, ...data };
|
|
180
|
-
this.router.replace({
|
|
181
|
-
query: {
|
|
182
|
-
...this.route.query,
|
|
183
|
-
...this.requestData.value
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
};
|
|
187
|
-
this.sort = (id) => {
|
|
188
|
-
const sortDir = this.requestData.value.sort === id && this.requestData.value.sortDir === "asc" ? "desc" : "asc";
|
|
189
|
-
this.updateRequest({ sort: id, sortDir });
|
|
190
|
-
};
|
|
191
|
-
this.updateFilters = (filters) => {
|
|
192
|
-
const filter = [];
|
|
193
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
194
|
-
if (!value) return;
|
|
195
|
-
const operator = value?.operator || "contains";
|
|
196
|
-
filter.push(`${key}:${value}:${operator}`);
|
|
197
|
-
});
|
|
198
|
-
this.updateRequest({ filter });
|
|
199
|
-
};
|
|
200
|
-
this.sorting = vue.computed(() => {
|
|
201
|
-
const requestData = this.requestData.value;
|
|
202
|
-
return {
|
|
203
|
-
sortColumn: requestData.sort,
|
|
204
|
-
sortDirection: requestData.sortDir ?? "asc"
|
|
205
|
-
};
|
|
206
|
-
});
|
|
207
|
-
this.filters = vue.computed(
|
|
208
|
-
() => jsonFormsCore.extractFilters(this.requestData.value.filter)
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
get httpRequest() {
|
|
212
|
-
return toolsVue.useApi();
|
|
213
|
-
}
|
|
214
|
-
reload() {
|
|
215
|
-
this._reload.value = Date.now();
|
|
216
|
-
}
|
|
217
|
-
updatePage(page) {
|
|
218
|
-
this.updateRequest({ page });
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const tableCache = /* @__PURE__ */ new Map();
|
|
222
|
-
const useTableStore = (name) => {
|
|
223
|
-
const tableStore = tableCache.get(name);
|
|
224
|
-
if (tableStore) {
|
|
225
|
-
return tableStore;
|
|
226
|
-
}
|
|
227
|
-
const newTableStore = new TableStore();
|
|
228
|
-
tableCache.set(name, newTableStore);
|
|
229
|
-
return newTableStore;
|
|
230
|
-
};
|
|
231
|
-
const _hoisted_1$7 = {
|
|
232
|
-
key: 0,
|
|
233
|
-
class: "mb-2"
|
|
234
|
-
};
|
|
235
|
-
const _sfc_main$g = /* @__PURE__ */ vue.defineComponent({
|
|
236
|
-
__name: "table.component",
|
|
237
|
-
props: TableComponentProperties,
|
|
238
|
-
emits: TableComponentEmits,
|
|
239
|
-
setup(__props, { emit: __emit }) {
|
|
240
|
-
const properties = __props;
|
|
241
|
-
const emit = __emit;
|
|
242
|
-
vue.watch(
|
|
243
|
-
() => properties.reload,
|
|
244
|
-
() => {
|
|
245
|
-
store.reload();
|
|
246
|
-
}
|
|
247
|
-
);
|
|
248
|
-
let store = useTableStore(properties.id);
|
|
249
|
-
vue.watch(
|
|
250
|
-
() => properties.uri,
|
|
251
|
-
() => {
|
|
252
|
-
store.init(properties.uri);
|
|
253
|
-
},
|
|
254
|
-
{ immediate: true }
|
|
255
|
-
);
|
|
256
|
-
const edit = (data) => {
|
|
257
|
-
emit("edit", data);
|
|
258
|
-
};
|
|
259
|
-
const deleteFn = (data) => {
|
|
260
|
-
emit("delete", data);
|
|
261
|
-
};
|
|
262
|
-
const components = {
|
|
263
|
-
TextCell: ui.TextCell,
|
|
264
|
-
BooleanCell: ui.BooleanCell
|
|
265
|
-
};
|
|
266
|
-
const displayColumns = vue.computed(() => {
|
|
267
|
-
return properties.uiSchema.elements.map((e) => {
|
|
268
|
-
const element = e;
|
|
269
|
-
const def = jsonFormsCore.findColumnDef(element, properties.schema);
|
|
270
|
-
const type = lodashEs.isArray(def.type) ? def.type[0] : def.type;
|
|
271
|
-
let component;
|
|
272
|
-
if (element.options?.format && element.options.format in components) {
|
|
273
|
-
component = components[element.options.format];
|
|
274
|
-
} else {
|
|
275
|
-
component = components[element.type];
|
|
276
|
-
}
|
|
277
|
-
if (!component) console.warn("No component found for type", element.type);
|
|
278
|
-
return {
|
|
279
|
-
...def,
|
|
280
|
-
type,
|
|
281
|
-
component
|
|
282
|
-
};
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
const onChangeFilters = (filters) => {
|
|
286
|
-
store.updateFilters(filters);
|
|
287
|
-
};
|
|
288
|
-
const onUpdatePage = (page) => {
|
|
289
|
-
store.updatePage(page);
|
|
290
|
-
};
|
|
291
|
-
const onSort = (id) => {
|
|
292
|
-
store.sort(id);
|
|
293
|
-
};
|
|
294
|
-
const sort = vue.computed(() => {
|
|
295
|
-
return store.sorting.value ?? { orderBy: "", ascending: 1 };
|
|
296
|
-
});
|
|
297
|
-
return (_ctx, _cache) => {
|
|
298
|
-
return vue.openBlock(), vue.createElementBlock("div", null, [
|
|
299
|
-
_ctx.filterUiSchema && _ctx.filterSchema ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$7, [
|
|
300
|
-
vue.createVNode(_sfc_main$h, {
|
|
301
|
-
layout: { uiSchema: _ctx.filterUiSchema, schema: _ctx.filterSchema },
|
|
302
|
-
filters: vue.unref(store).filters.value,
|
|
303
|
-
onChangeFilters
|
|
304
|
-
}, null, 8, ["layout", "filters"])
|
|
305
|
-
])) : vue.createCommentVNode("", true),
|
|
306
|
-
vue.createElementVNode("div", null, [
|
|
307
|
-
vue.createVNode(vue.unref(ui.Table), {
|
|
308
|
-
"display-columns": displayColumns.value,
|
|
309
|
-
sort: sort.value,
|
|
310
|
-
page: vue.unref(store).pageData.value,
|
|
311
|
-
loading: vue.unref(store).loading.value,
|
|
312
|
-
data: vue.unref(store).tableData.value,
|
|
313
|
-
actions: _ctx.actions,
|
|
314
|
-
onUpdatePage,
|
|
315
|
-
onDelete: deleteFn,
|
|
316
|
-
onEdit: edit,
|
|
317
|
-
onSort
|
|
318
|
-
}, null, 8, ["display-columns", "sort", "page", "loading", "data", "actions"])
|
|
19
|
+
vue.createElementVNode("div", _hoisted_2$8, [
|
|
20
|
+
vue.renderSlot(_ctx.$slots, "default")
|
|
319
21
|
])
|
|
320
|
-
]);
|
|
22
|
+
], 64);
|
|
321
23
|
};
|
|
322
24
|
}
|
|
323
25
|
});
|
|
324
|
-
const createRepository = (formSchemaModel, httpRequest, options = {}) => {
|
|
325
|
-
const notificationEntity = options.notification?.entityType || "entity";
|
|
326
|
-
const notificationStore = options.notification?.notification ?? null;
|
|
327
|
-
const getDataUri = (...suffix) => {
|
|
328
|
-
return [formSchemaModel.uri, ...suffix].join("/");
|
|
329
|
-
};
|
|
330
|
-
const handleSuccess = (message) => {
|
|
331
|
-
notificationStore?.success(message);
|
|
332
|
-
};
|
|
333
|
-
const handleError = (error, message) => {
|
|
334
|
-
console.error(error);
|
|
335
|
-
notificationStore?.error(message);
|
|
336
|
-
throw new Error(error);
|
|
337
|
-
};
|
|
338
|
-
const create = (object, options2) => {
|
|
339
|
-
return httpRequest.post(getDataUri(), object, options2).then((response) => {
|
|
340
|
-
handleSuccess(`Created ${notificationEntity}`);
|
|
341
|
-
return response.data;
|
|
342
|
-
}).catch((response) => {
|
|
343
|
-
handleError(response, `Failed to create ${notificationEntity}`);
|
|
344
|
-
});
|
|
345
|
-
};
|
|
346
|
-
const patch = (id, object, options2) => {
|
|
347
|
-
return httpRequest.patch(getDataUri(id), object, options2).then((response) => {
|
|
348
|
-
handleSuccess(`Saved ${notificationEntity}`);
|
|
349
|
-
return response.data;
|
|
350
|
-
}).catch((response) => {
|
|
351
|
-
handleError(response, `Failed to save ${notificationEntity}`);
|
|
352
|
-
});
|
|
353
|
-
};
|
|
354
|
-
const get = (id, options2) => {
|
|
355
|
-
return httpRequest.get(getDataUri(id), options2).then((response) => {
|
|
356
|
-
return response.data;
|
|
357
|
-
}).catch((response) => {
|
|
358
|
-
handleError(response, "Failed to load data");
|
|
359
|
-
});
|
|
360
|
-
};
|
|
361
|
-
const _delete = (id, options2) => {
|
|
362
|
-
return httpRequest.delete(getDataUri(id), options2).then((response) => {
|
|
363
|
-
handleSuccess(`${notificationEntity} deleted`);
|
|
364
|
-
return response;
|
|
365
|
-
}).catch((response) => {
|
|
366
|
-
handleError(response, `Failed to delete ${notificationEntity}`);
|
|
367
|
-
});
|
|
368
|
-
};
|
|
369
|
-
const createMulti = (objects, options2) => {
|
|
370
|
-
return Promise.all(
|
|
371
|
-
objects.map((object) => httpRequest.post(getDataUri(), object, options2))
|
|
372
|
-
).then((response) => {
|
|
373
|
-
handleSuccess(`Created ${notificationEntity}`);
|
|
374
|
-
return response.data;
|
|
375
|
-
}).catch((response) => {
|
|
376
|
-
handleError(response, `Failed to save ${notificationEntity}`);
|
|
377
|
-
});
|
|
378
|
-
};
|
|
379
|
-
return { create, patch, createMulti, delete: _delete, get };
|
|
380
|
-
};
|
|
381
|
-
const createFormEvents = (dispatch) => ({
|
|
382
|
-
dispatch
|
|
383
|
-
});
|
|
384
|
-
const FORM_EVENTS_KEY = /* @__PURE__ */ Symbol("json-forms:events");
|
|
385
|
-
const provideFormEvents = (dispatch) => {
|
|
386
|
-
const events = createFormEvents(dispatch);
|
|
387
|
-
vue.provide(FORM_EVENTS_KEY, events);
|
|
388
|
-
return events;
|
|
389
|
-
};
|
|
390
|
-
const useFormEvents = () => {
|
|
391
|
-
return vue.inject(
|
|
392
|
-
FORM_EVENTS_KEY,
|
|
393
|
-
createFormEvents(() => {
|
|
394
|
-
})
|
|
395
|
-
);
|
|
396
|
-
};
|
|
397
26
|
const errorPatterns = [
|
|
398
27
|
[/^Invalid input: expected \w+, received undefined$/, "This field is required"],
|
|
399
28
|
[/^Invalid input: expected \w+, received null$/, "This field is required"],
|
|
@@ -435,20 +64,13 @@ const registerZodErrorMap = () => {
|
|
|
435
64
|
return { message: `Must be at most ${i.maximum} characters` };
|
|
436
65
|
}
|
|
437
66
|
}
|
|
438
|
-
return { message: issue.message };
|
|
67
|
+
return { message: issue.message ?? "" };
|
|
439
68
|
}
|
|
440
69
|
});
|
|
441
70
|
};
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const rank = entry.tester(uischema, schema);
|
|
446
|
-
if (rank > -1 && (!best || rank > best.rank)) {
|
|
447
|
-
best = { rank, renderer: entry.renderer };
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
return best?.renderer ?? null;
|
|
451
|
-
}
|
|
71
|
+
const ERROR_MODE_KEY = /* @__PURE__ */ Symbol("errorMode");
|
|
72
|
+
const FORM_SUBMITTED_KEY = /* @__PURE__ */ Symbol("formSubmitted");
|
|
73
|
+
const FORM_READONLY_KEY = /* @__PURE__ */ Symbol("formReadonly");
|
|
452
74
|
const scopeToPath = (scope) => {
|
|
453
75
|
if (!scope) return "";
|
|
454
76
|
return scope.replace(/^#\//, "").split("/").filter((s) => s !== "properties").join(".");
|
|
@@ -457,86 +79,574 @@ const resolveSchema = (root, scope) => {
|
|
|
457
79
|
const segments = scope.replace(/^#\//, "").split("/").filter(Boolean);
|
|
458
80
|
return segments.reduce((acc, key) => acc?.[key], root);
|
|
459
81
|
};
|
|
460
|
-
const
|
|
82
|
+
const checkRequired = (rootSchema, scope, fieldName) => {
|
|
83
|
+
const segments = scope.replace(/^#\//, "").split("/");
|
|
84
|
+
const parentSegments = segments.slice(0, -2);
|
|
85
|
+
if (parentSegments.length === 0) {
|
|
86
|
+
const req2 = rootSchema?.required;
|
|
87
|
+
return Array.isArray(req2) && req2.includes(fieldName);
|
|
88
|
+
}
|
|
89
|
+
const parentScope = `#/${parentSegments.join("/")}`;
|
|
90
|
+
const parentSchema = resolveSchema(rootSchema, parentScope);
|
|
91
|
+
const req = parentSchema?.required;
|
|
92
|
+
return Array.isArray(req) && req.includes(fieldName);
|
|
93
|
+
};
|
|
94
|
+
const useInputProps = (uischema, schema, field, options = {}) => {
|
|
95
|
+
const rootSchema = vue.inject("rootSchema");
|
|
96
|
+
const path = scopeToPath(uischema.scope);
|
|
97
|
+
const { errorMessage, meta } = field;
|
|
98
|
+
const opts = uischema.options ?? {};
|
|
99
|
+
const labelFromScope = path.split(".").pop() ?? "";
|
|
100
|
+
const isRequired = rootSchema ? checkRequired(rootSchema, uischema.scope, labelFromScope) : false;
|
|
101
|
+
const s = schema ?? {};
|
|
102
|
+
const inferredType = (() => {
|
|
103
|
+
if (opts.format === "text") return "text";
|
|
104
|
+
if (s.format === "email") return "email";
|
|
105
|
+
if (s.format === "uri") return "url";
|
|
106
|
+
if (s.type === "number" || s.type === "integer") return "number";
|
|
107
|
+
return options.defaultType ?? "text";
|
|
108
|
+
})();
|
|
109
|
+
const styles = ui.mergeStyles(opts.styles);
|
|
110
|
+
const errorMode = vue.inject(ERROR_MODE_KEY, vue.ref("onBlur"));
|
|
111
|
+
const submitted = vue.inject(FORM_SUBMITTED_KEY, vue.ref(false));
|
|
112
|
+
const formReadonly = vue.inject(FORM_READONLY_KEY, vue.ref(false));
|
|
113
|
+
const shouldShowError = vue.computed(() => {
|
|
114
|
+
if (!errorMessage.value) return false;
|
|
115
|
+
switch (errorMode.value) {
|
|
116
|
+
case "always":
|
|
117
|
+
return true;
|
|
118
|
+
case "onChange":
|
|
119
|
+
return meta.dirty;
|
|
120
|
+
case "onSubmit":
|
|
121
|
+
return submitted.value;
|
|
122
|
+
case "onBlur":
|
|
123
|
+
default:
|
|
124
|
+
return meta.touched;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const width = opts.colspan || styles?.width === "full" ? "w-full" : opts.width ?? "min-w-input";
|
|
128
|
+
return vue.computed(() => ({
|
|
129
|
+
id: path,
|
|
130
|
+
placeholder: opts.placeholder,
|
|
131
|
+
description: s.description,
|
|
132
|
+
errors: shouldShowError.value ? opts.errorMessage ?? formatError(errorMessage.value) : void 0,
|
|
133
|
+
label: opts.label ?? labelFromScope.charAt(0).toUpperCase() + labelFromScope.slice(1),
|
|
134
|
+
visible: opts.visible ?? true,
|
|
135
|
+
required: isRequired,
|
|
136
|
+
enabled: opts.readonly !== true && !formReadonly.value,
|
|
137
|
+
isFocused: false,
|
|
138
|
+
isTouched: shouldShowError.value,
|
|
139
|
+
hideLabel: opts.hideLabel ?? false,
|
|
140
|
+
styles,
|
|
141
|
+
width,
|
|
142
|
+
type: inferredType,
|
|
143
|
+
...options.overrides
|
|
144
|
+
}));
|
|
145
|
+
};
|
|
146
|
+
const useCustomControlBinding = ({
|
|
147
|
+
useProps,
|
|
148
|
+
setDefaultValue
|
|
149
|
+
} = {}) => {
|
|
150
|
+
return (uischema, schema, options = {}) => {
|
|
151
|
+
const { values: formValues } = veeValidate.useFormContext();
|
|
152
|
+
const pathPrefix = vue.inject("pathPrefix", "");
|
|
153
|
+
const scopePath = scopeToPath(uischema.scope);
|
|
154
|
+
const path = pathPrefix ? `${pathPrefix}.${scopePath}` : scopePath;
|
|
155
|
+
const field = veeValidate.useField(() => path);
|
|
156
|
+
setDefaultValue?.(field);
|
|
157
|
+
const wrapper = useInputProps(uischema, schema, field, options);
|
|
158
|
+
const customWrapper = useProps?.(uischema, schema, field, options) ?? {
|
|
159
|
+
value: {}
|
|
160
|
+
};
|
|
161
|
+
const onBlur = () => field.handleBlur(new Event("blur"));
|
|
162
|
+
const onChange = () => field.handleChange(field.value.value);
|
|
163
|
+
let initialized = false;
|
|
164
|
+
vue.watch(field.value, (val) => {
|
|
165
|
+
if (!initialized) {
|
|
166
|
+
initialized = true;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
field.handleChange(val);
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
formValues,
|
|
173
|
+
uischema,
|
|
174
|
+
schema,
|
|
175
|
+
wrapper: vue.computed(() => ({ ...wrapper.value, ...customWrapper.value })),
|
|
176
|
+
value: field.value,
|
|
177
|
+
field,
|
|
178
|
+
onBlur,
|
|
179
|
+
onChange,
|
|
180
|
+
appliedOptions: vue.computed(
|
|
181
|
+
() => uischema.options ?? {}
|
|
182
|
+
)
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
};
|
|
186
|
+
const useControlBinding = (uischema, schema, options = {}) => {
|
|
187
|
+
return useCustomControlBinding()(uischema, schema, options);
|
|
188
|
+
};
|
|
189
|
+
const NOT_APPLICABLE = -1;
|
|
190
|
+
const rankWith = (rank, tester) => (uischema, schema, context) => tester(uischema, schema, context) ? rank : NOT_APPLICABLE;
|
|
191
|
+
const and = (...testers) => (uischema, schema, context) => testers.every((tester) => tester(uischema, schema, context));
|
|
192
|
+
const or = (...testers) => (uischema, schema, context) => testers.some((tester) => tester(uischema, schema, context));
|
|
193
|
+
const uiTypeIs = (expected) => (uischema) => !!uischema && uischema.type === expected;
|
|
194
|
+
const isControl = (uischema) => !!uischema && uischema.type === "Control";
|
|
195
|
+
const schemaTypeIs = (expected) => (uischema, schema) => isControl(uischema) && !!schema && schema.type === expected;
|
|
196
|
+
const optionIsIgnoreCase = (optionName, optionValue) => (uischema) => {
|
|
197
|
+
const options = uischema.options;
|
|
198
|
+
if (!options) return false;
|
|
199
|
+
const val = options[optionName];
|
|
200
|
+
return val?.toLowerCase() === optionValue?.toLowerCase();
|
|
201
|
+
};
|
|
202
|
+
const schemaFormatIsOneOf = (...formats) => (_uischema, schema) => {
|
|
203
|
+
const fmt = schema?.format;
|
|
204
|
+
return typeof fmt === "string" && formats.includes(fmt);
|
|
205
|
+
};
|
|
206
|
+
const anyOfTypeIs = (type) => (_uischema, schema) => {
|
|
207
|
+
const s = schema;
|
|
208
|
+
if (!Array.isArray(s?.anyOf)) return false;
|
|
209
|
+
return s.anyOf.some((sub) => sub?.type === type);
|
|
210
|
+
};
|
|
211
|
+
const isAutoCompleteControl = and(
|
|
212
|
+
// uiTypeIs('Control'),
|
|
213
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.autocomplete)
|
|
214
|
+
);
|
|
215
|
+
const isTextAreaControl = and(
|
|
216
|
+
uiTypeIs("Control"),
|
|
217
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.textArea)
|
|
218
|
+
);
|
|
219
|
+
const isStringFormat = and(
|
|
220
|
+
uiTypeIs("Control"),
|
|
221
|
+
or(
|
|
222
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.string),
|
|
223
|
+
schemaTypeIs("string"),
|
|
224
|
+
anyOfTypeIs("string")
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
const isMarkdownControl = and(
|
|
228
|
+
uiTypeIs("Control"),
|
|
229
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.markdown)
|
|
230
|
+
);
|
|
231
|
+
and(
|
|
232
|
+
uiTypeIs("Control"),
|
|
233
|
+
// schemaTypeIs('object'),
|
|
234
|
+
(uischema) => !uischema.options?.format
|
|
235
|
+
);
|
|
236
|
+
const isArrayRenderer = and(
|
|
237
|
+
schemaTypeIs("array")
|
|
238
|
+
// optionIsIgnoreCase('format', ControlType.array),
|
|
239
|
+
);
|
|
240
|
+
const isMultiselectControl = and(
|
|
241
|
+
uiTypeIs("Control"),
|
|
242
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.mutliSelect)
|
|
243
|
+
);
|
|
244
|
+
const isSelectControl = and(
|
|
245
|
+
uiTypeIs("Control"),
|
|
246
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.select)
|
|
247
|
+
);
|
|
248
|
+
const isBooleanControl = or(
|
|
249
|
+
and(uiTypeIs("Control"), schemaTypeIs("boolean")),
|
|
250
|
+
and(uiTypeIs("Control"), optionIsIgnoreCase("format", jsonFormsCore.ControlType.boolean))
|
|
251
|
+
);
|
|
252
|
+
const isNumberFormat = and(
|
|
253
|
+
uiTypeIs("Control"),
|
|
254
|
+
or(optionIsIgnoreCase("format", jsonFormsCore.ControlType.number), schemaTypeIs("number"))
|
|
255
|
+
);
|
|
256
|
+
const isIntegerFormat = and(
|
|
257
|
+
uiTypeIs("Control"),
|
|
258
|
+
or(
|
|
259
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.integer),
|
|
260
|
+
schemaTypeIs("integer")
|
|
261
|
+
)
|
|
262
|
+
);
|
|
263
|
+
and(
|
|
264
|
+
uiTypeIs("Control"),
|
|
265
|
+
or(
|
|
266
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.date),
|
|
267
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.dateTime),
|
|
268
|
+
schemaFormatIsOneOf("date", "date-time")
|
|
269
|
+
)
|
|
270
|
+
);
|
|
271
|
+
and(
|
|
272
|
+
uiTypeIs("Control"),
|
|
273
|
+
optionIsIgnoreCase("format", jsonFormsCore.ControlType.relation)
|
|
274
|
+
);
|
|
275
|
+
const EMPTY_VALUE = "—";
|
|
276
|
+
const DisplayValueProperties = {
|
|
277
|
+
direction: { type: String, default: "ltr" },
|
|
278
|
+
path: { type: String, required: true },
|
|
279
|
+
displayValue: { type: Object, required: true },
|
|
280
|
+
options: { type: Object, required: true },
|
|
281
|
+
value: { type: Object, required: true }
|
|
282
|
+
};
|
|
283
|
+
const _hoisted_1$j = { class: "text-gray-500 text-sm" };
|
|
284
|
+
const _sfc_main$t = /* @__PURE__ */ vue.defineComponent({
|
|
285
|
+
__name: "NotSetValue",
|
|
286
|
+
props: DisplayValueProperties,
|
|
287
|
+
setup(__props) {
|
|
288
|
+
return (_ctx, _cache) => {
|
|
289
|
+
return vue.openBlock(), vue.createElementBlock("p", _hoisted_1$j, [
|
|
290
|
+
vue.createTextVNode(vue.toDisplayString(vue.unref(EMPTY_VALUE)) + " · ", 1),
|
|
291
|
+
_cache[0] || (_cache[0] = vue.createElementVNode("small", null, "Not set", -1))
|
|
292
|
+
]);
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
const _hoisted_1$i = {
|
|
297
|
+
key: 0,
|
|
298
|
+
class: "inline-flex items-center gap-1 text-success"
|
|
299
|
+
};
|
|
300
|
+
const _hoisted_2$7 = {
|
|
461
301
|
key: 1,
|
|
462
|
-
class: "
|
|
302
|
+
class: "inline-flex items-center gap-1 text-base-content/50"
|
|
463
303
|
};
|
|
464
|
-
const _sfc_main$
|
|
465
|
-
__name: "
|
|
466
|
-
props:
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
304
|
+
const _sfc_main$s = /* @__PURE__ */ vue.defineComponent({
|
|
305
|
+
__name: "BooleanValue",
|
|
306
|
+
props: DisplayValueProperties,
|
|
307
|
+
setup(__props) {
|
|
308
|
+
return (_ctx, _cache) => {
|
|
309
|
+
return _ctx.displayValue === true ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_1$i, [
|
|
310
|
+
vue.createVNode(vue.unref(ui.Icon), {
|
|
311
|
+
icon: vue.unref(ui.IconEnum).Check,
|
|
312
|
+
size: "sm"
|
|
313
|
+
}, null, 8, ["icon"]),
|
|
314
|
+
_cache[0] || (_cache[0] = vue.createElementVNode("span", null, "Yes", -1))
|
|
315
|
+
])) : _ctx.displayValue === false ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_2$7, [
|
|
316
|
+
vue.createVNode(vue.unref(ui.Icon), {
|
|
317
|
+
icon: vue.unref(ui.IconEnum).Close,
|
|
318
|
+
size: "sm"
|
|
319
|
+
}, null, 8, ["icon"]),
|
|
320
|
+
_cache[1] || (_cache[1] = vue.createElementVNode("span", null, "No", -1))
|
|
321
|
+
])) : (vue.openBlock(), vue.createBlock(_sfc_main$t, { key: 2 }));
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
const UNITS = [
|
|
326
|
+
["year", 60 * 60 * 24 * 365],
|
|
327
|
+
["month", 60 * 60 * 24 * 30],
|
|
328
|
+
["day", 60 * 60 * 24],
|
|
329
|
+
["hour", 60 * 60],
|
|
330
|
+
["minute", 60],
|
|
331
|
+
["second", 1]
|
|
332
|
+
];
|
|
333
|
+
const relativeFromNow = (date, now, locale) => {
|
|
334
|
+
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
|
|
335
|
+
const diffSeconds = Math.round((date.getTime() - now.getTime()) / 1e3);
|
|
336
|
+
const abs = Math.abs(diffSeconds);
|
|
337
|
+
for (const [unit, secondsInUnit] of UNITS) {
|
|
338
|
+
if (abs >= secondsInUnit || unit === "second") {
|
|
339
|
+
return rtf.format(Math.round(diffSeconds / secondsInUnit), unit);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return rtf.format(0, "second");
|
|
343
|
+
};
|
|
344
|
+
const formatDate = (value, {
|
|
345
|
+
withTime = true,
|
|
346
|
+
locale = "en-GB",
|
|
347
|
+
timeZone = "UTC"
|
|
348
|
+
} = {}) => {
|
|
349
|
+
const date = parseDate(value);
|
|
350
|
+
if (!date) return null;
|
|
351
|
+
const datePart = new Intl.DateTimeFormat(locale, {
|
|
352
|
+
day: "numeric",
|
|
353
|
+
month: "short",
|
|
354
|
+
year: "numeric",
|
|
355
|
+
timeZone
|
|
356
|
+
}).format(date);
|
|
357
|
+
let out = datePart;
|
|
358
|
+
if (withTime) {
|
|
359
|
+
const timePart = new Intl.DateTimeFormat(locale, {
|
|
360
|
+
hour: "2-digit",
|
|
361
|
+
minute: "2-digit",
|
|
362
|
+
timeZone
|
|
363
|
+
}).format(date);
|
|
364
|
+
out = `${datePart}, ${timePart}`;
|
|
365
|
+
}
|
|
366
|
+
return out;
|
|
367
|
+
};
|
|
368
|
+
const relativeDate = (value, { locale = "en-GB", now } = {}) => {
|
|
369
|
+
const date = parseDate(value);
|
|
370
|
+
if (!date) return;
|
|
371
|
+
return relativeFromNow(date, now ?? /* @__PURE__ */ new Date(), locale);
|
|
372
|
+
};
|
|
373
|
+
const parseDate = (value) => {
|
|
374
|
+
if (value == null || value === "") return null;
|
|
375
|
+
const date = value instanceof Date ? value : new Date(String(value));
|
|
376
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
377
|
+
return date;
|
|
378
|
+
};
|
|
379
|
+
const isDate = (value) => {
|
|
380
|
+
return !!parseDate(value);
|
|
381
|
+
};
|
|
382
|
+
const _hoisted_1$h = ["dir"];
|
|
383
|
+
const _hoisted_2$6 = { class: "text-gray-500" };
|
|
384
|
+
const _sfc_main$r = /* @__PURE__ */ vue.defineComponent({
|
|
385
|
+
__name: "DateValue",
|
|
386
|
+
props: DisplayValueProperties,
|
|
387
|
+
setup(__props) {
|
|
388
|
+
return (_ctx, _cache) => {
|
|
389
|
+
return vue.openBlock(), vue.createElementBlock("p", { dir: _ctx.direction }, [
|
|
390
|
+
vue.createTextVNode(vue.toDisplayString(vue.unref(formatDate)(_ctx.displayValue)) + " · ", 1),
|
|
391
|
+
vue.createElementVNode("small", _hoisted_2$6, vue.toDisplayString(vue.unref(relativeDate)(_ctx.displayValue)), 1)
|
|
392
|
+
], 8, _hoisted_1$h);
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
const _hoisted_1$g = ["href"];
|
|
397
|
+
const _sfc_main$q = /* @__PURE__ */ vue.defineComponent({
|
|
398
|
+
__name: "LinkValue",
|
|
399
|
+
props: DisplayValueProperties,
|
|
400
|
+
setup(__props) {
|
|
401
|
+
return (_ctx, _cache) => {
|
|
402
|
+
return vue.openBlock(), vue.createElementBlock("a", {
|
|
403
|
+
href: _ctx.displayValue,
|
|
404
|
+
target: "_blank",
|
|
405
|
+
rel: "noopener noreferrer"
|
|
406
|
+
}, vue.toDisplayString(_ctx.displayValue), 9, _hoisted_1$g);
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
const _hoisted_1$f = ["innerHTML"];
|
|
411
|
+
const _sfc_main$p = /* @__PURE__ */ vue.defineComponent({
|
|
412
|
+
__name: "MarkdownValue",
|
|
413
|
+
props: DisplayValueProperties,
|
|
414
|
+
setup(__props) {
|
|
415
|
+
const props = __props;
|
|
416
|
+
const renderedHtml = vue.computed(() => {
|
|
417
|
+
const raw = props.value;
|
|
418
|
+
if (!raw) return "";
|
|
419
|
+
return raw.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<br>");
|
|
420
|
+
});
|
|
421
|
+
return (_ctx, _cache) => {
|
|
422
|
+
return vue.openBlock(), vue.createElementBlock("div", {
|
|
423
|
+
class: "prose prose-sm max-w-none",
|
|
424
|
+
innerHTML: renderedHtml.value
|
|
425
|
+
}, null, 8, _hoisted_1$f);
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
const _hoisted_1$e = { key: 0 };
|
|
430
|
+
const _sfc_main$o = /* @__PURE__ */ vue.defineComponent({
|
|
431
|
+
__name: "NumberValue",
|
|
432
|
+
props: DisplayValueProperties,
|
|
433
|
+
setup(__props) {
|
|
434
|
+
return (_ctx, _cache) => {
|
|
435
|
+
return _ctx.displayValue ? (vue.openBlock(), vue.createElementBlock("p", _hoisted_1$e, vue.toDisplayString(_ctx.displayValue), 1)) : (vue.openBlock(), vue.createBlock(_sfc_main$t, { key: 1 }));
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
const _hoisted_1$d = {
|
|
440
|
+
class: "block overflow-auto text-sm",
|
|
441
|
+
style: { "max-height": "200px" }
|
|
442
|
+
};
|
|
443
|
+
const _sfc_main$n = /* @__PURE__ */ vue.defineComponent({
|
|
444
|
+
__name: "ObjectValue",
|
|
445
|
+
props: DisplayValueProperties,
|
|
446
|
+
setup(__props) {
|
|
447
|
+
return (_ctx, _cache) => {
|
|
448
|
+
return vue.openBlock(), vue.createElementBlock("pre", _hoisted_1$d, " " + vue.toDisplayString(_ctx.displayValue) + "\n\n\n ", 1);
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
const _hoisted_1$c = ["dir"];
|
|
453
|
+
const _sfc_main$m = /* @__PURE__ */ vue.defineComponent({
|
|
454
|
+
__name: "StringValue",
|
|
455
|
+
props: DisplayValueProperties,
|
|
456
|
+
setup(__props) {
|
|
457
|
+
return (_ctx, _cache) => {
|
|
458
|
+
return _ctx.displayValue ? (vue.openBlock(), vue.createElementBlock("p", {
|
|
459
|
+
key: 0,
|
|
460
|
+
dir: _ctx.direction
|
|
461
|
+
}, vue.toDisplayString(_ctx.displayValue), 9, _hoisted_1$c)) : (vue.openBlock(), vue.createBlock(_sfc_main$t, { key: 1 }));
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
const createFormEvents = (dispatch) => ({
|
|
466
|
+
dispatch
|
|
467
|
+
});
|
|
468
|
+
const FORM_EVENTS_KEY = /* @__PURE__ */ Symbol("json-forms:events");
|
|
469
|
+
const provideFormEvents = (dispatch) => {
|
|
470
|
+
const events = createFormEvents(dispatch);
|
|
471
|
+
vue.provide(FORM_EVENTS_KEY, events);
|
|
472
|
+
return events;
|
|
473
|
+
};
|
|
474
|
+
const useFormEvents = () => {
|
|
475
|
+
return vue.inject(
|
|
476
|
+
FORM_EVENTS_KEY,
|
|
477
|
+
createFormEvents(() => {
|
|
478
|
+
})
|
|
479
|
+
);
|
|
480
|
+
};
|
|
481
|
+
const _hoisted_1$b = { class: "px-2 flex gap-2 items-center" };
|
|
482
|
+
const _hoisted_2$5 = { class: "truncate" };
|
|
483
|
+
const _sfc_main$l = /* @__PURE__ */ vue.defineComponent({
|
|
484
|
+
__name: "ViewDetailValue",
|
|
485
|
+
props: DisplayValueProperties,
|
|
471
486
|
setup(__props) {
|
|
472
487
|
const props = __props;
|
|
473
|
-
const
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (!u.scope) return props.schema;
|
|
483
|
-
const fromRoot = resolveSchema(rootSchema, u.scope);
|
|
484
|
-
if (fromRoot) return fromRoot;
|
|
485
|
-
return resolveSchema(props.schema, u.scope) ?? props.schema;
|
|
486
|
-
});
|
|
487
|
-
const renderer = vue.computed(
|
|
488
|
-
() => findRenderer(registry, props.uischema, resolved.value)
|
|
489
|
-
);
|
|
488
|
+
const formEvents = useFormEvents();
|
|
489
|
+
const view = () => {
|
|
490
|
+
formEvents.dispatch({
|
|
491
|
+
event: "view",
|
|
492
|
+
type: props.path,
|
|
493
|
+
data: props.value,
|
|
494
|
+
options: props.options
|
|
495
|
+
});
|
|
496
|
+
};
|
|
490
497
|
return (_ctx, _cache) => {
|
|
491
|
-
return
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
498
|
+
return vue.openBlock(), vue.createBlock(vue.unref(ui.Btn), {
|
|
499
|
+
color: "ghost",
|
|
500
|
+
class: "border-gray-300 text-primary h-8",
|
|
501
|
+
tooltip: "View",
|
|
502
|
+
onClick: view
|
|
503
|
+
}, {
|
|
504
|
+
default: vue.withCtx(() => [
|
|
505
|
+
vue.createElementVNode("span", _hoisted_1$b, [
|
|
506
|
+
vue.createElementVNode("span", _hoisted_2$5, vue.toDisplayString(_ctx.displayValue), 1),
|
|
507
|
+
vue.createVNode(vue.unref(ui.Icon), {
|
|
508
|
+
icon: vue.unref(ui.IconEnum).View,
|
|
509
|
+
size: "sm"
|
|
510
|
+
}, null, 8, ["icon"])
|
|
511
|
+
])
|
|
512
|
+
]),
|
|
513
|
+
_: 1
|
|
514
|
+
});
|
|
496
515
|
};
|
|
497
516
|
}
|
|
498
517
|
});
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
518
|
+
const isLink = (value) => value?.startsWith?.("http");
|
|
519
|
+
const getDirection = (options, formValues) => {
|
|
520
|
+
const pathPrefix = vue.inject("pathPrefix", "");
|
|
521
|
+
const opts = options ?? {};
|
|
522
|
+
if (!opts.directionField) return opts.direction ?? "ltr";
|
|
523
|
+
const key = pathPrefix ? `${pathPrefix}.${opts.directionField}` : opts.directionField;
|
|
524
|
+
const val = key.split(".").reduce((o, k) => o?.[k], formValues);
|
|
525
|
+
return val ?? "ltr";
|
|
526
|
+
};
|
|
527
|
+
const getSelectValue = (value, opts) => {
|
|
528
|
+
const labelKey = opts.labelKey ?? "label";
|
|
529
|
+
const valueKey = opts.valueKey ?? "value";
|
|
530
|
+
const resolvedKey = value?.[valueKey] ?? value;
|
|
531
|
+
if (typeof resolvedKey === "string" && opts.values)
|
|
532
|
+
return opts.values?.find((o) => o[valueKey] === resolvedKey)?.[labelKey] ?? resolvedKey;
|
|
533
|
+
const resolved = value && typeof value === "object" ? value?.[labelKey] ?? value[valueKey] : value;
|
|
534
|
+
if (!resolved) return null;
|
|
535
|
+
return resolved;
|
|
536
|
+
};
|
|
537
|
+
const getNestedValue = (value, opts, formValues) => {
|
|
538
|
+
if (opts.dataPath) {
|
|
539
|
+
const nested = opts.dataPath.split(".").reduce((o, k) => o?.[k], formValues);
|
|
540
|
+
if (opts.key && nested && typeof nested === "object")
|
|
541
|
+
return nested[opts.key] ?? null;
|
|
542
|
+
return nested ?? null;
|
|
516
543
|
}
|
|
544
|
+
if (value !== null && typeof value === "object" && opts.key) {
|
|
545
|
+
return value[opts.key] ?? null;
|
|
546
|
+
}
|
|
547
|
+
return value ?? null;
|
|
517
548
|
};
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
"
|
|
521
|
-
|
|
522
|
-
"
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
549
|
+
const getDisplayValue = (value, formValues, opts) => {
|
|
550
|
+
const format = opts.format ?? "date";
|
|
551
|
+
if (format === "select") return getSelectValue(value, opts);
|
|
552
|
+
if (format === "autocomplete") return getSelectValue(value, opts);
|
|
553
|
+
if (typeof value !== "object") {
|
|
554
|
+
return value ?? null;
|
|
555
|
+
}
|
|
556
|
+
return getNestedValue(value, opts, formValues);
|
|
557
|
+
};
|
|
558
|
+
const _getComponent = (value, options, schema, uiSchema) => {
|
|
559
|
+
if (isNumberFormat(uiSchema, schema)) return _sfc_main$o;
|
|
560
|
+
if (isIntegerFormat(uiSchema, schema)) return _sfc_main$o;
|
|
561
|
+
if (isMarkdownControl(uiSchema, schema)) return _sfc_main$p;
|
|
562
|
+
if (value === null || value === void 0) return _sfc_main$t;
|
|
563
|
+
if (options.resource) return _sfc_main$l;
|
|
564
|
+
if (isDate(value)) return _sfc_main$r;
|
|
565
|
+
if (isLink(value)) return _sfc_main$q;
|
|
566
|
+
if (typeof value === "boolean") return _sfc_main$s;
|
|
567
|
+
if (typeof value === "object") return _sfc_main$n;
|
|
568
|
+
return _sfc_main$m;
|
|
569
|
+
};
|
|
570
|
+
const getComponent = (path, uischema, schema, field, formValues) => {
|
|
571
|
+
return vue.computed(() => {
|
|
572
|
+
const value = field.value?.value;
|
|
573
|
+
const options = uischema.options ?? {};
|
|
574
|
+
const displayValue = getDisplayValue(value, formValues, options);
|
|
575
|
+
return {
|
|
576
|
+
component: _getComponent(value, options, schema, uischema),
|
|
577
|
+
value: {
|
|
578
|
+
value,
|
|
579
|
+
path,
|
|
580
|
+
displayValue,
|
|
581
|
+
options,
|
|
582
|
+
direction: getDirection(options, formValues)
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
});
|
|
586
|
+
};
|
|
587
|
+
const useCustomReadonlyControlBinding = ({
|
|
588
|
+
useProps,
|
|
589
|
+
setDefaultValue
|
|
590
|
+
} = {}) => {
|
|
591
|
+
return (uischema, schema, options = {}) => {
|
|
592
|
+
const { values: formValues } = veeValidate.useFormContext();
|
|
593
|
+
const pathPrefix = vue.inject("pathPrefix", "");
|
|
594
|
+
const scopePath = scopeToPath(uischema.scope);
|
|
595
|
+
const path = pathPrefix ? `${pathPrefix}.${scopePath}` : scopePath;
|
|
596
|
+
const field = veeValidate.useField(() => path);
|
|
597
|
+
const wrapper = useInputProps(uischema, schema, field, options);
|
|
598
|
+
const customWrapper = useProps?.(uischema, schema, field, options) ?? {
|
|
599
|
+
value: {}
|
|
600
|
+
};
|
|
601
|
+
return {
|
|
602
|
+
wrapper: vue.computed(() => ({ ...wrapper.value, ...customWrapper.value })),
|
|
603
|
+
appliedOptions: vue.computed(
|
|
604
|
+
() => uischema.options ?? {}
|
|
605
|
+
),
|
|
606
|
+
displayWrapper: getComponent(path, uischema, schema, field, formValues)
|
|
607
|
+
};
|
|
608
|
+
};
|
|
609
|
+
};
|
|
610
|
+
const useReadonlyControlBinding = (uischema, schema, options = {}) => {
|
|
611
|
+
return useCustomReadonlyControlBinding()(uischema, schema, options);
|
|
612
|
+
};
|
|
613
|
+
const useDisplayValue = (value, formValues, opts) => {
|
|
614
|
+
const raw = value;
|
|
615
|
+
if (typeof raw !== "object") {
|
|
616
|
+
return raw ?? null;
|
|
617
|
+
}
|
|
618
|
+
if (opts.dataPath) {
|
|
619
|
+
const nested = opts.dataPath.split(".").reduce((o, k) => o?.[k], formValues);
|
|
620
|
+
if (opts.key && nested && typeof nested === "object")
|
|
621
|
+
return nested[opts.key] ?? null;
|
|
622
|
+
return nested ?? null;
|
|
623
|
+
}
|
|
624
|
+
if (raw !== null && typeof raw === "object" && opts.key) {
|
|
625
|
+
return raw[opts.key] ?? null;
|
|
626
|
+
}
|
|
627
|
+
return raw ?? null;
|
|
628
|
+
};
|
|
629
|
+
const HTTP_CLIENT_KEY = /* @__PURE__ */ Symbol("json-forms:http-client");
|
|
630
|
+
const provideHttpClient = (http) => {
|
|
631
|
+
vue.provide(HTTP_CLIENT_KEY, http);
|
|
632
|
+
};
|
|
633
|
+
const useHttpClient = () => {
|
|
634
|
+
const http = vue.inject(HTTP_CLIENT_KEY);
|
|
635
|
+
if (!http) {
|
|
636
|
+
throw new Error(
|
|
637
|
+
"[json-forms] No HttpClient provided. Pass an `http` prop to <JsonForm> or call provideHttpClient() in a parent component."
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
return http;
|
|
641
|
+
};
|
|
642
|
+
const _hoisted_1$a = { class: "flex-1" };
|
|
643
|
+
const _hoisted_2$4 = { key: 0 };
|
|
644
|
+
const _hoisted_3$2 = {
|
|
535
645
|
key: 0,
|
|
536
646
|
class: "text-sm text-base-content/50"
|
|
537
647
|
};
|
|
538
|
-
const _hoisted_4 = { key: 1 };
|
|
539
|
-
const _sfc_main$
|
|
648
|
+
const _hoisted_4$2 = { key: 1 };
|
|
649
|
+
const _sfc_main$k = /* @__PURE__ */ vue.defineComponent({
|
|
540
650
|
__name: "ArrayRenderer",
|
|
541
651
|
props: {
|
|
542
652
|
uischema: {},
|
|
@@ -579,7 +689,7 @@ const _sfc_main$e = /* @__PURE__ */ vue.defineComponent({
|
|
|
579
689
|
vue.unref(layout) === "row" ? "flex-row items-center" : "flex-col"
|
|
580
690
|
])
|
|
581
691
|
}, [
|
|
582
|
-
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(fields), (entry,
|
|
692
|
+
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(fields), (entry, index) => {
|
|
583
693
|
return vue.openBlock(), vue.createElementBlock("div", {
|
|
584
694
|
key: entry.key,
|
|
585
695
|
class: "flex-1"
|
|
@@ -590,29 +700,29 @@ const _sfc_main$e = /* @__PURE__ */ vue.defineComponent({
|
|
|
590
700
|
vue.unref(layout) === "row" ? "flex-col" : "flex-row items-center"
|
|
591
701
|
])
|
|
592
702
|
}, [
|
|
593
|
-
vue.createElementVNode("div", _hoisted_1$
|
|
703
|
+
vue.createElementVNode("div", _hoisted_1$a, [
|
|
594
704
|
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(childElements.value, (child, ci) => {
|
|
595
|
-
return vue.openBlock(), vue.createBlock(_sfc_main$
|
|
705
|
+
return vue.openBlock(), vue.createBlock(_sfc_main$5, {
|
|
596
706
|
key: ci,
|
|
597
707
|
uischema: child,
|
|
598
708
|
schema: vue.unref(itemSchema),
|
|
599
|
-
"path-prefix": `${vue.unref(path)}[${
|
|
709
|
+
"path-prefix": `${vue.unref(path)}[${index}]`
|
|
600
710
|
}, null, 8, ["uischema", "schema", "path-prefix"]);
|
|
601
711
|
}), 128))
|
|
602
712
|
]),
|
|
603
|
-
showActions.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$
|
|
713
|
+
showActions.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$4, [
|
|
604
714
|
vue.unref(fields).length > 1 ? (vue.openBlock(), vue.createBlock(vue.unref(ui.Btn), {
|
|
605
715
|
key: 0,
|
|
606
716
|
icon: vue.unref(ui.IconEnum).Delete,
|
|
607
717
|
outline: true,
|
|
608
|
-
onClick: ($event) => vue.unref(remove)(
|
|
718
|
+
onClick: ($event) => vue.unref(remove)(index)
|
|
609
719
|
}, null, 8, ["icon", "onClick"])) : vue.createCommentVNode("", true)
|
|
610
720
|
])) : vue.createCommentVNode("", true)
|
|
611
721
|
], 2)
|
|
612
722
|
]);
|
|
613
723
|
}), 128)),
|
|
614
|
-
vue.unref(fields).length === 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, " No data ")) : vue.createCommentVNode("", true),
|
|
615
|
-
showActions.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4, [
|
|
724
|
+
vue.unref(fields).length === 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$2, " No data ")) : vue.createCommentVNode("", true),
|
|
725
|
+
showActions.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4$2, [
|
|
616
726
|
vue.createVNode(vue.unref(ui.Btn), {
|
|
617
727
|
icon: vue.unref(ui.IconEnum).Plus,
|
|
618
728
|
outline: true,
|
|
@@ -629,50 +739,21 @@ const _sfc_main$e = /* @__PURE__ */ vue.defineComponent({
|
|
|
629
739
|
};
|
|
630
740
|
}
|
|
631
741
|
});
|
|
632
|
-
const isAutoCompleteControl = testers.and(
|
|
633
|
-
// uiTypeIs('Control'),
|
|
634
|
-
testers.optionIs("format", jsonFormsCore.ControlType.autocomplete)
|
|
635
|
-
);
|
|
636
|
-
const isTextAreaControl = testers.and(
|
|
637
|
-
core$1.uiTypeIs("Control"),
|
|
638
|
-
testers.optionIs("format", jsonFormsCore.ControlType.textArea)
|
|
639
|
-
);
|
|
640
|
-
const isStringFormat = testers.and(
|
|
641
|
-
core$1.uiTypeIs("Control"),
|
|
642
|
-
testers.or(testers.optionIs("format", jsonFormsCore.ControlType.string), testers.schemaTypeIs("string"))
|
|
643
|
-
);
|
|
644
|
-
const isMarkdownControl = testers.and(
|
|
645
|
-
core$1.uiTypeIs("Control"),
|
|
646
|
-
testers.optionIs("format", jsonFormsCore.ControlType.markdown)
|
|
647
|
-
);
|
|
648
|
-
const isArrayRenderer = testers.and(
|
|
649
|
-
testers.schemaTypeIs("array")
|
|
650
|
-
// optionIs('format', ControlType.array),
|
|
651
|
-
);
|
|
652
|
-
const isMultiselectControl = testers.and(
|
|
653
|
-
core$1.uiTypeIs("Control"),
|
|
654
|
-
testers.optionIs("format", jsonFormsCore.ControlType.mutliSelect)
|
|
655
|
-
);
|
|
656
|
-
const isSelectControl = testers.and(
|
|
657
|
-
core$1.uiTypeIs("Control"),
|
|
658
|
-
testers.optionIs("format", jsonFormsCore.ControlType.select)
|
|
659
|
-
);
|
|
660
|
-
const isBooleanControl = testers.or(
|
|
661
|
-
testers.isBooleanControl,
|
|
662
|
-
testers.and(core$1.uiTypeIs("Control"), testers.optionIs("format", jsonFormsCore.ControlType.boolean))
|
|
663
|
-
);
|
|
664
|
-
const isNumberFormat = testers.and(
|
|
665
|
-
core$1.uiTypeIs("Control"),
|
|
666
|
-
testers.or(testers.optionIs("format", jsonFormsCore.ControlType.number), testers.schemaTypeIs("number"))
|
|
667
|
-
);
|
|
668
|
-
const isIntegerFormat = testers.and(
|
|
669
|
-
core$1.uiTypeIs("Control"),
|
|
670
|
-
testers.or(testers.optionIs("format", jsonFormsCore.ControlType.integer), testers.schemaTypeIs("integer"))
|
|
671
|
-
);
|
|
672
742
|
const arrayRenderers = [
|
|
673
|
-
{ tester:
|
|
743
|
+
{ tester: rankWith(12, isArrayRenderer), renderer: _sfc_main$k }
|
|
674
744
|
];
|
|
675
|
-
const MethodSchema = zod.z.enum([
|
|
745
|
+
const MethodSchema = zod.z.enum([
|
|
746
|
+
"get",
|
|
747
|
+
"post",
|
|
748
|
+
"delete",
|
|
749
|
+
"put",
|
|
750
|
+
"patch",
|
|
751
|
+
"GET",
|
|
752
|
+
"POST",
|
|
753
|
+
"DELETE",
|
|
754
|
+
"PUT",
|
|
755
|
+
"PATCH"
|
|
756
|
+
]);
|
|
676
757
|
const OperationSchema = zod.z.object({
|
|
677
758
|
uri: zod.z.string(),
|
|
678
759
|
method: MethodSchema
|
|
@@ -693,22 +774,24 @@ const OperationMap = {
|
|
|
693
774
|
findAll: "get",
|
|
694
775
|
findOne: "get",
|
|
695
776
|
lookup: "get",
|
|
696
|
-
update: "
|
|
777
|
+
update: "patch"
|
|
697
778
|
};
|
|
779
|
+
const schemaDef = zod.z.object({
|
|
780
|
+
ui: zod.z.any().optional(),
|
|
781
|
+
data: zod.z.any()
|
|
782
|
+
});
|
|
698
783
|
const ResourceSchema = zod.z.object({
|
|
699
784
|
id: zod.z.string(),
|
|
700
785
|
uri: zod.z.string(),
|
|
701
786
|
operations: Operations,
|
|
702
|
-
schema:
|
|
703
|
-
|
|
704
|
-
|
|
787
|
+
schema: schemaDef.optional(),
|
|
788
|
+
schemas: zod.z.object({
|
|
789
|
+
form: schemaDef.optional()
|
|
705
790
|
}).optional()
|
|
706
791
|
}).transform((data) => {
|
|
707
|
-
const schema = data.schema;
|
|
708
|
-
if (schema) {
|
|
709
|
-
|
|
710
|
-
schema.ui = jsonFormsCore.uiFromJsonSchema(schema.data);
|
|
711
|
-
}
|
|
792
|
+
const schema = data.schema ?? data.schemas?.form ?? { ui: void 0, data: void 0 };
|
|
793
|
+
if (!schema.ui && schema.data) {
|
|
794
|
+
schema.ui = jsonFormsCore.uiFromJsonSchema(schema.data);
|
|
712
795
|
}
|
|
713
796
|
const operations = {};
|
|
714
797
|
for (const k in OperationMap) {
|
|
@@ -716,14 +799,14 @@ const ResourceSchema = zod.z.object({
|
|
|
716
799
|
const defaultOperation = OperationMap[key];
|
|
717
800
|
const operation = data.operations[key];
|
|
718
801
|
const mapResourceSchema = () => {
|
|
719
|
-
if (
|
|
802
|
+
if (operation === void 0 || operation === false) return null;
|
|
720
803
|
if (operation === true)
|
|
721
804
|
return { uri: data.uri, method: defaultOperation };
|
|
722
805
|
if (typeof operation === "string")
|
|
723
806
|
return { uri: operation, method: "get" };
|
|
724
807
|
return {
|
|
725
808
|
uri: operation.uri,
|
|
726
|
-
method: operation.method ?? defaultOperation
|
|
809
|
+
method: operation.method?.toLowerCase() ?? defaultOperation
|
|
727
810
|
};
|
|
728
811
|
};
|
|
729
812
|
operations[key] = mapResourceSchema();
|
|
@@ -734,58 +817,72 @@ const ResourceSchema = zod.z.object({
|
|
|
734
817
|
operations
|
|
735
818
|
};
|
|
736
819
|
});
|
|
737
|
-
const getResourceSchema = async (resourceUri,
|
|
738
|
-
|
|
739
|
-
return fetch.get(resourceUri).then((response) => {
|
|
820
|
+
const getResourceSchema = async (resourceUri, http) => {
|
|
821
|
+
return http.get(resourceUri).then((response) => {
|
|
740
822
|
const resource = ResourceSchema.safeParse(response.data);
|
|
741
823
|
if (!resource.success)
|
|
742
824
|
throw new Error(`Invalid resource schema: ${resource.error}`);
|
|
743
825
|
return resource.data;
|
|
744
826
|
});
|
|
745
827
|
};
|
|
746
|
-
const
|
|
828
|
+
const resolvePlaceholders = (uri, formValues, searchTerm) => {
|
|
829
|
+
const resolved = uri.replace(/\{form\.([^}]+)\}/g, (_, key) => {
|
|
830
|
+
const value = key.split(".").reduce((o, k) => o?.[k], formValues);
|
|
831
|
+
return encodeURIComponent(value ?? "");
|
|
832
|
+
});
|
|
833
|
+
if (resolved.includes("{q}"))
|
|
834
|
+
return resolved.replace("{q}", encodeURIComponent(searchTerm));
|
|
835
|
+
if (resolved.includes("{text}"))
|
|
836
|
+
return resolved.replace("{text}", searchTerm);
|
|
837
|
+
return `${resolved}${encodeURIComponent(searchTerm)}`;
|
|
838
|
+
};
|
|
839
|
+
const useRemoteOption = (options, http, formValues) => {
|
|
747
840
|
return {
|
|
748
841
|
fetchOptions: (searchTerm, signal) => {
|
|
749
|
-
const
|
|
750
|
-
return
|
|
842
|
+
const uri = resolvePlaceholders(options.uri, formValues, searchTerm);
|
|
843
|
+
return http.get(uri, { signal }).then((data) => {
|
|
844
|
+
const body = data.data;
|
|
845
|
+
if (Array.isArray(body)) return body;
|
|
846
|
+
return body[options.dataField ?? "data"];
|
|
847
|
+
});
|
|
751
848
|
}
|
|
752
849
|
};
|
|
753
850
|
};
|
|
754
|
-
const useResourceOptions = async (options) => {
|
|
755
|
-
const resource = await getResourceSchema(
|
|
756
|
-
options.resource,
|
|
757
|
-
options.skipAuth ?? false
|
|
758
|
-
);
|
|
759
|
-
const fetch = options.skipAuth ? axios : toolsVue.useApi();
|
|
851
|
+
const useResourceOptions = async (options, http, formValues) => {
|
|
852
|
+
const resource = await getResourceSchema(options.resource, http);
|
|
760
853
|
const lookup = resource.operations.lookup;
|
|
761
854
|
return {
|
|
762
855
|
fetchOptions: (searchTerm, signal) => {
|
|
763
|
-
const uri = lookup.uri
|
|
856
|
+
const uri = resolvePlaceholders(lookup.uri, formValues, searchTerm);
|
|
764
857
|
const method = lookup.method;
|
|
765
|
-
return
|
|
766
|
-
(data) => data.data[options.dataField ?? "data"]
|
|
767
|
-
);
|
|
858
|
+
return http[method](uri, { signal }).then((data) => data.data[options.dataField ?? "data"]);
|
|
768
859
|
},
|
|
769
|
-
enableCreate: !!resource.operations.create,
|
|
860
|
+
enableCreate: !!resource.operations.create && resource.schema.ui,
|
|
770
861
|
form: resource.operations.create ? {
|
|
771
862
|
ui_schema: resource.schema.ui,
|
|
772
863
|
json_schema: resource.schema.data,
|
|
773
864
|
title: `Create new ${resource.id}`,
|
|
774
865
|
create: async (data) => {
|
|
775
866
|
const create = resource.operations.create;
|
|
776
|
-
return
|
|
777
|
-
(result) => result.data
|
|
778
|
-
);
|
|
867
|
+
return http[create.method](create.uri, data).then((result) => result.data);
|
|
779
868
|
}
|
|
780
869
|
} : null
|
|
781
870
|
};
|
|
782
871
|
};
|
|
783
|
-
const useFetchOptions = async (options) => {
|
|
872
|
+
const useFetchOptions = async (options, http, formValues = {}) => {
|
|
784
873
|
let config = {};
|
|
785
|
-
if (options.uri)
|
|
786
|
-
config = useRemoteOption(
|
|
787
|
-
|
|
788
|
-
|
|
874
|
+
if ("uri" in options && options.uri)
|
|
875
|
+
config = useRemoteOption(
|
|
876
|
+
options,
|
|
877
|
+
http,
|
|
878
|
+
formValues
|
|
879
|
+
);
|
|
880
|
+
if ("resource" in options && options.resource)
|
|
881
|
+
config = await useResourceOptions(
|
|
882
|
+
options,
|
|
883
|
+
http,
|
|
884
|
+
formValues
|
|
885
|
+
);
|
|
789
886
|
return {
|
|
790
887
|
fetchOptions: null,
|
|
791
888
|
labelKey: options.labelKey,
|
|
@@ -795,116 +892,41 @@ const useFetchOptions = async (options) => {
|
|
|
795
892
|
...config
|
|
796
893
|
};
|
|
797
894
|
};
|
|
798
|
-
const
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
const parentScope = `#/${parentSegments.join("/")}`;
|
|
806
|
-
const parentSchema = resolveSchema(rootSchema, parentScope);
|
|
807
|
-
const req = parentSchema?.required;
|
|
808
|
-
return Array.isArray(req) && req.includes(fieldName);
|
|
809
|
-
};
|
|
810
|
-
const useInputProps = (uischema, schema, field, options = {}) => {
|
|
811
|
-
const rootSchema = vue.inject("rootSchema");
|
|
812
|
-
const path = scopeToPath(uischema.scope);
|
|
813
|
-
const { errorMessage, meta } = field;
|
|
814
|
-
const opts = uischema.options ?? {};
|
|
815
|
-
const labelFromScope = path.split(".").pop() ?? "";
|
|
816
|
-
const isRequired = rootSchema ? checkRequired(rootSchema, uischema.scope, labelFromScope) : false;
|
|
817
|
-
const s = schema ?? {};
|
|
818
|
-
const inferredType = (() => {
|
|
819
|
-
if (opts.format === "text") return "text";
|
|
820
|
-
if (s.format === "email") return "email";
|
|
821
|
-
if (s.format === "uri") return "url";
|
|
822
|
-
if (s.type === "number" || s.type === "integer") return "number";
|
|
823
|
-
return options.defaultType ?? "text";
|
|
824
|
-
})();
|
|
825
|
-
const styles = ui.mergeStyles(opts.styles);
|
|
826
|
-
const errorMode = vue.inject(ERROR_MODE_KEY, vue.ref("onBlur"));
|
|
827
|
-
const submitted = vue.inject(FORM_SUBMITTED_KEY, vue.ref(false));
|
|
828
|
-
const shouldShowError = vue.computed(() => {
|
|
829
|
-
if (!errorMessage.value) return false;
|
|
830
|
-
switch (errorMode.value) {
|
|
831
|
-
case "always":
|
|
832
|
-
return true;
|
|
833
|
-
case "onChange":
|
|
834
|
-
return meta.dirty;
|
|
835
|
-
case "onSubmit":
|
|
836
|
-
return submitted.value;
|
|
837
|
-
case "onBlur":
|
|
838
|
-
default:
|
|
839
|
-
return meta.touched;
|
|
840
|
-
}
|
|
841
|
-
});
|
|
842
|
-
const width = opts.colspan || styles?.width === "full" ? "w-full" : opts.width ?? "min-w-input";
|
|
843
|
-
return vue.computed(() => ({
|
|
844
|
-
id: path,
|
|
845
|
-
placeholder: opts.placeholder,
|
|
846
|
-
description: s.description,
|
|
847
|
-
errors: shouldShowError.value ? opts.errorMessage ?? formatError(errorMessage.value) : void 0,
|
|
848
|
-
label: opts.label ?? labelFromScope.charAt(0).toUpperCase() + labelFromScope.slice(1),
|
|
849
|
-
visible: opts.visible ?? true,
|
|
850
|
-
required: isRequired,
|
|
851
|
-
enabled: opts.readonly !== true,
|
|
852
|
-
isFocused: false,
|
|
853
|
-
isTouched: shouldShowError.value,
|
|
854
|
-
hideLabel: opts.hideLabel ?? false,
|
|
855
|
-
styles,
|
|
856
|
-
width,
|
|
857
|
-
type: inferredType,
|
|
858
|
-
...options.overrides
|
|
859
|
-
}));
|
|
860
|
-
};
|
|
861
|
-
const useCustomControlBinding = ({
|
|
862
|
-
useProps,
|
|
863
|
-
setDefaultValue
|
|
864
|
-
} = {}) => {
|
|
865
|
-
return (uischema, schema, options = {}) => {
|
|
866
|
-
const pathPrefix = vue.inject("pathPrefix", "");
|
|
867
|
-
const scopePath = scopeToPath(uischema.scope);
|
|
868
|
-
const path = pathPrefix ? `${pathPrefix}.${scopePath}` : scopePath;
|
|
869
|
-
const field = veeValidate.useField(() => path);
|
|
870
|
-
setDefaultValue?.(field);
|
|
871
|
-
const wrapper = useInputProps(uischema, schema, field, options);
|
|
872
|
-
const customWrapper = useProps?.(uischema, schema, field, options) ?? {
|
|
873
|
-
value: {}
|
|
874
|
-
};
|
|
875
|
-
const onBlur = () => field.handleBlur(new Event("blur"));
|
|
876
|
-
const onChange = () => field.handleChange(field.value.value);
|
|
877
|
-
let initialized = false;
|
|
878
|
-
vue.watch(field.value, (val) => {
|
|
879
|
-
if (!initialized) {
|
|
880
|
-
initialized = true;
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
field.handleChange(val);
|
|
884
|
-
});
|
|
885
|
-
return {
|
|
886
|
-
wrapper: vue.computed(() => ({ ...wrapper.value, ...customWrapper.value })),
|
|
887
|
-
value: field.value,
|
|
888
|
-
field,
|
|
889
|
-
onBlur,
|
|
890
|
-
onChange,
|
|
891
|
-
appliedOptions: vue.computed(
|
|
892
|
-
() => uischema.options ?? {}
|
|
893
|
-
)
|
|
894
|
-
};
|
|
895
|
-
};
|
|
896
|
-
};
|
|
897
|
-
const useControlBinding = (uischema, schema, options = {}) => {
|
|
898
|
-
return useCustomControlBinding()(uischema, schema, options);
|
|
895
|
+
const loadDisplayValue = (options, value) => {
|
|
896
|
+
const values = options.values;
|
|
897
|
+
if (!values) return value;
|
|
898
|
+
const valueKey = options.valueKey;
|
|
899
|
+
const id = value?.[valueKey] ?? value;
|
|
900
|
+
const findValue = values.find((o) => o[valueKey] === id);
|
|
901
|
+
return findValue;
|
|
899
902
|
};
|
|
900
903
|
const useSelectInput = (...fields) => (uischema, schema, field) => {
|
|
901
904
|
const opts = uischema.options ?? {};
|
|
902
905
|
return vue.computed(() => {
|
|
903
|
-
|
|
906
|
+
const options = Object.fromEntries(
|
|
907
|
+
fields.filter((f) => f in opts).map((f) => [f, opts[f]])
|
|
908
|
+
);
|
|
909
|
+
if (!options.labelKey) options.labelKey = "label";
|
|
910
|
+
if (!options.valueKey) options.valueKey = "value";
|
|
911
|
+
options.displayValue = loadDisplayValue(
|
|
912
|
+
options,
|
|
913
|
+
field.value.value
|
|
914
|
+
);
|
|
915
|
+
return options;
|
|
904
916
|
});
|
|
905
917
|
};
|
|
906
918
|
const useSelectBinding = useCustomControlBinding({
|
|
907
|
-
useProps: useSelectInput(
|
|
919
|
+
useProps: useSelectInput(
|
|
920
|
+
"options",
|
|
921
|
+
"values",
|
|
922
|
+
"uri",
|
|
923
|
+
"resource",
|
|
924
|
+
"dataField",
|
|
925
|
+
"labelKey",
|
|
926
|
+
"valueKey",
|
|
927
|
+
"clearable",
|
|
928
|
+
"storeValue"
|
|
929
|
+
)
|
|
908
930
|
});
|
|
909
931
|
const useAutocompleteBinding = useCustomControlBinding({
|
|
910
932
|
useProps: useSelectInput(
|
|
@@ -918,7 +940,58 @@ const useAutocompleteBinding = useCustomControlBinding({
|
|
|
918
940
|
"skipAuth"
|
|
919
941
|
)
|
|
920
942
|
});
|
|
921
|
-
const
|
|
943
|
+
const FormModalProperties = {
|
|
944
|
+
/** Title displayed in the modal header. */
|
|
945
|
+
modalTitle: { type: String, required: true },
|
|
946
|
+
/** Label for the save button. */
|
|
947
|
+
saveLabel: { type: String, default: "Save" },
|
|
948
|
+
/** Label for the cancel button. */
|
|
949
|
+
cancelLabel: { type: String, default: "Cancel" },
|
|
950
|
+
/** JSON schema describing the shape of the form data. */
|
|
951
|
+
schema: { type: Object, required: true },
|
|
952
|
+
/** UI schema describing the layout and controls. */
|
|
953
|
+
uiSchema: { type: Object, required: true },
|
|
954
|
+
/** Modal width (`'xs'`, `'sm'`, `'md'`, `'lg'`, `'xl'`). */
|
|
955
|
+
modalSize: { type: String, default: "md" },
|
|
956
|
+
/** Callback invoked when the modal closes (with result or `null` on cancel). */
|
|
957
|
+
onClose: {
|
|
958
|
+
type: Function,
|
|
959
|
+
required: true
|
|
960
|
+
},
|
|
961
|
+
/** Callback for form events dispatched by custom renderers. */
|
|
962
|
+
onEvents: {
|
|
963
|
+
type: Function
|
|
964
|
+
},
|
|
965
|
+
/** Initial form data to populate the form with. */
|
|
966
|
+
data: { type: Object, required: true },
|
|
967
|
+
/** When validation errors are shown. */
|
|
968
|
+
errorMode: {
|
|
969
|
+
type: String,
|
|
970
|
+
default: "onBlur"
|
|
971
|
+
},
|
|
972
|
+
/** HTTP client passed through to the inner JsonForm for remote renderers (e.g. autocomplete). */
|
|
973
|
+
http: {
|
|
974
|
+
type: Object,
|
|
975
|
+
default: null
|
|
976
|
+
},
|
|
977
|
+
/** Custom renderer registry passed to the inner JsonForm. */
|
|
978
|
+
renderers: {
|
|
979
|
+
type: Array,
|
|
980
|
+
default: null
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
const FormModalEmits = [
|
|
984
|
+
/** Emitted when the modal is closed (submit or cancel). */
|
|
985
|
+
"closeModal",
|
|
986
|
+
/** Emitted when a custom renderer dispatches a form event. */
|
|
987
|
+
"events",
|
|
988
|
+
/** Emitted when validation errors change. */
|
|
989
|
+
"errors",
|
|
990
|
+
/** Emitted when form validity changes. */
|
|
991
|
+
"valid"
|
|
992
|
+
];
|
|
993
|
+
const _hoisted_1$9 = { class: "overflow-auto" };
|
|
994
|
+
const _sfc_main$j = /* @__PURE__ */ vue.defineComponent({
|
|
922
995
|
__name: "FormModal",
|
|
923
996
|
props: /* @__PURE__ */ vue.mergeModels(FormModalProperties, {
|
|
924
997
|
"modelValue": {},
|
|
@@ -927,7 +1000,7 @@ const _sfc_main$d = /* @__PURE__ */ vue.defineComponent({
|
|
|
927
1000
|
emits: /* @__PURE__ */ vue.mergeModels(FormModalEmits, ["update:modelValue"]),
|
|
928
1001
|
setup(__props, { emit: __emit }) {
|
|
929
1002
|
const properties = __props;
|
|
930
|
-
const id = `
|
|
1003
|
+
const id = `edit_${Math.floor(Math.random() * 1e3)}`;
|
|
931
1004
|
const formRef = vue.ref();
|
|
932
1005
|
const valid = vue.ref(false);
|
|
933
1006
|
const formData = vue.useModel(__props, "modelValue");
|
|
@@ -949,7 +1022,7 @@ const _sfc_main$d = /* @__PURE__ */ vue.defineComponent({
|
|
|
949
1022
|
};
|
|
950
1023
|
const onErrors = (errors) => {
|
|
951
1024
|
emits("errors", errors);
|
|
952
|
-
valid.value =
|
|
1025
|
+
valid.value = !errors || (Array.isArray(errors) ? errors.length === 0 : Object.keys(errors).length === 0);
|
|
953
1026
|
};
|
|
954
1027
|
vue.watch(valid, (newValid, oldValid) => {
|
|
955
1028
|
if (newValid !== oldValid) {
|
|
@@ -964,43 +1037,215 @@ const _sfc_main$d = /* @__PURE__ */ vue.defineComponent({
|
|
|
964
1037
|
onCloseModal: onCancel
|
|
965
1038
|
}), {
|
|
966
1039
|
content: vue.withCtx(() => [
|
|
967
|
-
vue.
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1040
|
+
vue.createElementVNode("div", _hoisted_1$9, [
|
|
1041
|
+
vue.renderSlot(_ctx.$slots, "content-before"),
|
|
1042
|
+
vue.createVNode(_sfc_main$4, {
|
|
1043
|
+
id: `modal-${id}`,
|
|
1044
|
+
ref_key: "formRef",
|
|
1045
|
+
ref: formRef,
|
|
1046
|
+
"form-data": formData.value,
|
|
1047
|
+
schema: _ctx.schema,
|
|
1048
|
+
"ui-schema": _ctx.uiSchema,
|
|
1049
|
+
"error-mode": _ctx.errorMode,
|
|
1050
|
+
http: properties.http,
|
|
1051
|
+
renderers: properties.renderers,
|
|
1052
|
+
onErrors,
|
|
1053
|
+
onChange,
|
|
1054
|
+
onEvents: _cache[0] || (_cache[0] = ($event) => emits("events", $event))
|
|
1055
|
+
}, null, 8, ["id", "form-data", "schema", "ui-schema", "error-mode", "http", "renderers"]),
|
|
1056
|
+
vue.renderSlot(_ctx.$slots, "content-after")
|
|
1057
|
+
])
|
|
981
1058
|
]),
|
|
982
1059
|
actions: vue.withCtx(() => [
|
|
983
1060
|
vue.createVNode(vue.unref(ui.Btn), {
|
|
984
1061
|
color: vue.unref(ui.Color).secondary,
|
|
985
1062
|
outline: true,
|
|
986
|
-
"aria-label":
|
|
1063
|
+
"aria-label": _ctx.cancelLabel,
|
|
987
1064
|
onClick: onCancel
|
|
988
1065
|
}, {
|
|
989
|
-
default: vue.withCtx(() => [
|
|
990
|
-
vue.createTextVNode(
|
|
991
|
-
])
|
|
1066
|
+
default: vue.withCtx(() => [
|
|
1067
|
+
vue.createTextVNode(vue.toDisplayString(_ctx.cancelLabel), 1)
|
|
1068
|
+
]),
|
|
992
1069
|
_: 1
|
|
993
|
-
}, 8, ["color"]),
|
|
1070
|
+
}, 8, ["color", "aria-label"]),
|
|
994
1071
|
vue.createVNode(vue.unref(ui.Btn), {
|
|
995
1072
|
disabled: !valid.value,
|
|
996
|
-
"aria-label":
|
|
1073
|
+
"aria-label": _ctx.saveLabel,
|
|
997
1074
|
onClick: onSubmit
|
|
998
1075
|
}, {
|
|
999
|
-
default: vue.withCtx(() => [
|
|
1000
|
-
vue.createTextVNode(
|
|
1001
|
-
])
|
|
1076
|
+
default: vue.withCtx(() => [
|
|
1077
|
+
vue.createTextVNode(vue.toDisplayString(_ctx.saveLabel), 1)
|
|
1078
|
+
]),
|
|
1079
|
+
_: 1
|
|
1080
|
+
}, 8, ["disabled", "aria-label"])
|
|
1081
|
+
]),
|
|
1082
|
+
_: 3
|
|
1083
|
+
}, 16, ["width"]);
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
const ViewModalProperties = {
|
|
1088
|
+
/** Title displayed in the modal header. */
|
|
1089
|
+
modalTitle: { type: String, required: true },
|
|
1090
|
+
/** Label for the close button. */
|
|
1091
|
+
closeLabel: { type: String, default: "Close" },
|
|
1092
|
+
/** Label for the edit button. Only rendered when `canEdit` is true. */
|
|
1093
|
+
editLabel: { type: String, default: "Edit" },
|
|
1094
|
+
/** Label for the delete button. Only rendered when `canDelete` is true. */
|
|
1095
|
+
deleteLabel: { type: String, default: "Delete" },
|
|
1096
|
+
/** JSON schema describing the shape of the form data. */
|
|
1097
|
+
schema: { type: Object, required: true },
|
|
1098
|
+
/** UI schema describing the layout and controls. */
|
|
1099
|
+
uiSchema: { type: Object, required: true },
|
|
1100
|
+
/** Modal width (`'xs'`, `'sm'`, `'md'`, `'lg'`, `'xl'`). */
|
|
1101
|
+
modalSize: { type: String, default: "md" },
|
|
1102
|
+
/** Callback invoked when the modal closes (with result or `null` on cancel). */
|
|
1103
|
+
onClose: {
|
|
1104
|
+
type: Function,
|
|
1105
|
+
default: () => {
|
|
1106
|
+
}
|
|
1107
|
+
},
|
|
1108
|
+
/**
|
|
1109
|
+
* Show the Edit button.
|
|
1110
|
+
* The caller wires the action by listening to the `edit` event via `onEdit`
|
|
1111
|
+
* in the props object — Vue's v-bind spread in modalWrapper converts onXxx
|
|
1112
|
+
* keys into event handlers automatically.
|
|
1113
|
+
*/
|
|
1114
|
+
canEdit: { type: Boolean, default: false },
|
|
1115
|
+
/**
|
|
1116
|
+
* Show the Delete button.
|
|
1117
|
+
* The caller wires the action by listening to the `delete` event via `onDelete`
|
|
1118
|
+
* in the props object.
|
|
1119
|
+
*/
|
|
1120
|
+
canDelete: { type: Boolean, default: false },
|
|
1121
|
+
/** Initial form data to populate the form with. */
|
|
1122
|
+
data: { type: Object, required: true },
|
|
1123
|
+
/** Custom renderer registry passed to the inner JsonForm. */
|
|
1124
|
+
renderers: {
|
|
1125
|
+
type: Array,
|
|
1126
|
+
default: null
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
const ViewModalEmits = [
|
|
1130
|
+
/** Emitted when the modal is closed. */
|
|
1131
|
+
"closeModal",
|
|
1132
|
+
/** Emitted when the Edit button is clicked. Payload: current form data. */
|
|
1133
|
+
"edit",
|
|
1134
|
+
/** Emitted when the Delete button is clicked. Payload: current form data. */
|
|
1135
|
+
"delete",
|
|
1136
|
+
/** Emitted when the view event is fired from the form. Payload: payload of the form event */
|
|
1137
|
+
"view"
|
|
1138
|
+
];
|
|
1139
|
+
const _hoisted_1$8 = { class: "text-gray-500 text-xs mb-2" };
|
|
1140
|
+
const _hoisted_2$3 = { class: "overflow-y-auto" };
|
|
1141
|
+
const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
1142
|
+
__name: "ViewModal",
|
|
1143
|
+
props: /* @__PURE__ */ vue.mergeModels(ViewModalProperties, {
|
|
1144
|
+
"modelValue": {},
|
|
1145
|
+
"modelModifiers": {}
|
|
1146
|
+
}),
|
|
1147
|
+
emits: /* @__PURE__ */ vue.mergeModels(ViewModalEmits, ["update:modelValue"]),
|
|
1148
|
+
setup(__props, { emit: __emit }) {
|
|
1149
|
+
const properties = __props;
|
|
1150
|
+
const id = `view_${Math.floor(Math.random() * 1e3)}`;
|
|
1151
|
+
const valid = vue.ref(false);
|
|
1152
|
+
const formData = vue.useModel(__props, "modelValue");
|
|
1153
|
+
const emits = __emit;
|
|
1154
|
+
if (properties.data) {
|
|
1155
|
+
formData.value = properties.data;
|
|
1156
|
+
}
|
|
1157
|
+
vue.provide(
|
|
1158
|
+
"renderers",
|
|
1159
|
+
properties.renderers?.length ? [...customRenderers, ...properties.renderers] : customRenderers
|
|
1160
|
+
);
|
|
1161
|
+
vue.provide("readonlyRenderers", properties.renderers ?? []);
|
|
1162
|
+
vue.provide("rootSchema", properties.schema);
|
|
1163
|
+
vue.provide("styles", ui.myStyles);
|
|
1164
|
+
const onCancel = () => {
|
|
1165
|
+
formData.value = {};
|
|
1166
|
+
emits("closeModal", null);
|
|
1167
|
+
};
|
|
1168
|
+
const onEditClick = () => {
|
|
1169
|
+
const data = formData.value;
|
|
1170
|
+
emits("edit", data);
|
|
1171
|
+
emits("closeModal", null);
|
|
1172
|
+
};
|
|
1173
|
+
const onDeleteClick = () => {
|
|
1174
|
+
const data = formData.value;
|
|
1175
|
+
emits("delete", data);
|
|
1176
|
+
emits("closeModal", null);
|
|
1177
|
+
};
|
|
1178
|
+
vue.watch(valid, (newValid, oldValid) => {
|
|
1179
|
+
if (newValid !== oldValid) {
|
|
1180
|
+
emits("valid", newValid);
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1183
|
+
const handleEvent = (event) => {
|
|
1184
|
+
if (event.event !== "view") return;
|
|
1185
|
+
emits("view", event);
|
|
1186
|
+
};
|
|
1187
|
+
return (_ctx, _cache) => {
|
|
1188
|
+
return vue.openBlock(), vue.createBlock(vue.unref(ui.Modal), vue.mergeProps(properties, {
|
|
1189
|
+
open: true,
|
|
1190
|
+
"disable-close": false,
|
|
1191
|
+
width: _ctx.modalSize,
|
|
1192
|
+
onCloseModal: onCancel
|
|
1193
|
+
}), {
|
|
1194
|
+
title: vue.withCtx(() => [
|
|
1195
|
+
vue.createElementVNode("h3", null, vue.toDisplayString(_ctx.modalTitle), 1),
|
|
1196
|
+
vue.createElementVNode("div", _hoisted_1$8, vue.toDisplayString(formData.value.id), 1)
|
|
1197
|
+
]),
|
|
1198
|
+
content: vue.withCtx(() => [
|
|
1199
|
+
vue.renderSlot(_ctx.$slots, "content-before"),
|
|
1200
|
+
vue.createElementVNode("div", _hoisted_2$3, [
|
|
1201
|
+
vue.createVNode(_sfc_main$4, {
|
|
1202
|
+
id: `modal-${id}`,
|
|
1203
|
+
ref: "formRef",
|
|
1204
|
+
"form-data": formData.value,
|
|
1205
|
+
schema: _ctx.schema,
|
|
1206
|
+
readonly: true,
|
|
1207
|
+
"ui-schema": _ctx.uiSchema,
|
|
1208
|
+
renderers: properties.renderers,
|
|
1209
|
+
onEvents: handleEvent
|
|
1210
|
+
}, null, 8, ["id", "form-data", "schema", "ui-schema", "renderers"])
|
|
1211
|
+
]),
|
|
1212
|
+
vue.renderSlot(_ctx.$slots, "content-after")
|
|
1213
|
+
]),
|
|
1214
|
+
actions: vue.withCtx(() => [
|
|
1215
|
+
_ctx.canEdit ? (vue.openBlock(), vue.createBlock(vue.unref(ui.Btn), {
|
|
1216
|
+
key: 0,
|
|
1217
|
+
"aria-label": _ctx.editLabel,
|
|
1218
|
+
onClick: onEditClick
|
|
1219
|
+
}, {
|
|
1220
|
+
default: vue.withCtx(() => [
|
|
1221
|
+
vue.createTextVNode(vue.toDisplayString(_ctx.editLabel), 1)
|
|
1222
|
+
]),
|
|
1223
|
+
_: 1
|
|
1224
|
+
}, 8, ["aria-label"])) : vue.createCommentVNode("", true),
|
|
1225
|
+
vue.createVNode(vue.unref(ui.Btn), {
|
|
1226
|
+
color: vue.unref(ui.Color).secondary,
|
|
1227
|
+
outline: true,
|
|
1228
|
+
"aria-label": _ctx.closeLabel,
|
|
1229
|
+
onClick: onCancel
|
|
1230
|
+
}, {
|
|
1231
|
+
default: vue.withCtx(() => [
|
|
1232
|
+
vue.createTextVNode(vue.toDisplayString(_ctx.closeLabel), 1)
|
|
1233
|
+
]),
|
|
1002
1234
|
_: 1
|
|
1003
|
-
}, 8, ["
|
|
1235
|
+
}, 8, ["color", "aria-label"]),
|
|
1236
|
+
_ctx.canDelete ? (vue.openBlock(), vue.createBlock(vue.unref(ui.Btn), {
|
|
1237
|
+
key: 1,
|
|
1238
|
+
color: vue.unref(ui.Color).error,
|
|
1239
|
+
outline: true,
|
|
1240
|
+
"aria-label": _ctx.deleteLabel,
|
|
1241
|
+
icon: vue.unref(ui.IconEnum).Delete,
|
|
1242
|
+
onClick: onDeleteClick
|
|
1243
|
+
}, {
|
|
1244
|
+
default: vue.withCtx(() => [
|
|
1245
|
+
vue.createTextVNode(vue.toDisplayString(_ctx.deleteLabel), 1)
|
|
1246
|
+
]),
|
|
1247
|
+
_: 1
|
|
1248
|
+
}, 8, ["color", "aria-label", "icon"])) : vue.createCommentVNode("", true)
|
|
1004
1249
|
]),
|
|
1005
1250
|
_: 3
|
|
1006
1251
|
}, 16, ["width"]);
|
|
@@ -1015,10 +1260,12 @@ class JsonFormModalService {
|
|
|
1015
1260
|
uiSchema,
|
|
1016
1261
|
modalSize,
|
|
1017
1262
|
onClose,
|
|
1018
|
-
onEvents
|
|
1263
|
+
onEvents,
|
|
1264
|
+
http,
|
|
1265
|
+
renderers
|
|
1019
1266
|
}) {
|
|
1020
1267
|
ui.ModalService.openModal({
|
|
1021
|
-
component: _sfc_main$
|
|
1268
|
+
component: _sfc_main$j,
|
|
1022
1269
|
props: {
|
|
1023
1270
|
schema,
|
|
1024
1271
|
uiSchema,
|
|
@@ -1026,12 +1273,49 @@ class JsonFormModalService {
|
|
|
1026
1273
|
data: initialData ?? {},
|
|
1027
1274
|
modalTitle,
|
|
1028
1275
|
onClose,
|
|
1029
|
-
onEvents
|
|
1276
|
+
onEvents,
|
|
1277
|
+
http,
|
|
1278
|
+
renderers
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
static openViewModal({
|
|
1283
|
+
data,
|
|
1284
|
+
modalTitle,
|
|
1285
|
+
schema,
|
|
1286
|
+
uiSchema,
|
|
1287
|
+
modalSize,
|
|
1288
|
+
onClose,
|
|
1289
|
+
onEdit,
|
|
1290
|
+
onDelete,
|
|
1291
|
+
renderers,
|
|
1292
|
+
onView
|
|
1293
|
+
}) {
|
|
1294
|
+
ui.ModalService.openModal({
|
|
1295
|
+
component: _sfc_main$i,
|
|
1296
|
+
props: {
|
|
1297
|
+
schema,
|
|
1298
|
+
uiSchema,
|
|
1299
|
+
modalSize,
|
|
1300
|
+
data,
|
|
1301
|
+
modalTitle,
|
|
1302
|
+
onClose: onClose ?? (() => {
|
|
1303
|
+
}),
|
|
1304
|
+
// Boolean props drive button visibility — no `on` prefix so Vue passes
|
|
1305
|
+
// them as regular props, not event listeners.
|
|
1306
|
+
canEdit: !!onEdit,
|
|
1307
|
+
canDelete: !!onDelete,
|
|
1308
|
+
// `onEdit`/`onDelete` are intercepted by Vue's v-bind spread as event
|
|
1309
|
+
// handlers for ViewModal's declared 'edit'/'delete' emits.
|
|
1310
|
+
onEdit,
|
|
1311
|
+
onDelete,
|
|
1312
|
+
renderers,
|
|
1313
|
+
onView
|
|
1030
1314
|
}
|
|
1031
1315
|
});
|
|
1032
1316
|
}
|
|
1033
1317
|
}
|
|
1034
|
-
const _sfc_main$
|
|
1318
|
+
const _sfc_main$h = /* @__PURE__ */ vue.defineComponent({
|
|
1035
1319
|
__name: "AutocompleteControlRenderer",
|
|
1036
1320
|
props: {
|
|
1037
1321
|
uischema: {},
|
|
@@ -1047,12 +1331,16 @@ const _sfc_main$c = /* @__PURE__ */ vue.defineComponent({
|
|
|
1047
1331
|
onChange: onFieldChange,
|
|
1048
1332
|
appliedOptions
|
|
1049
1333
|
} = useAutocompleteBinding(props.uischema, props.schema);
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1334
|
+
const http = useHttpClient();
|
|
1335
|
+
const { values: formValues } = veeValidate.useFormContext();
|
|
1336
|
+
const fetchOptions = vue.ref(null);
|
|
1337
|
+
vue.watch(
|
|
1338
|
+
[appliedOptions, formValues],
|
|
1339
|
+
async ([opts]) => {
|
|
1340
|
+
fetchOptions.value = await useFetchOptions(opts, http, formValues);
|
|
1341
|
+
},
|
|
1342
|
+
{ immediate: true, deep: true }
|
|
1343
|
+
);
|
|
1056
1344
|
const onChange = (val) => {
|
|
1057
1345
|
setValue(val);
|
|
1058
1346
|
onFieldChange();
|
|
@@ -1065,12 +1353,17 @@ const _sfc_main$c = /* @__PURE__ */ vue.defineComponent({
|
|
|
1065
1353
|
return;
|
|
1066
1354
|
}
|
|
1067
1355
|
const { valueKey, labelKey } = fetchOptions.value;
|
|
1356
|
+
const opts = appliedOptions.value;
|
|
1357
|
+
if (opts.storeValue && valueKey && valueKey in result) {
|
|
1358
|
+
field.setValue(result[valueKey]);
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1068
1361
|
const keys = [valueKey, labelKey].filter(Boolean);
|
|
1069
1362
|
if (keys.length === 0) {
|
|
1070
1363
|
field.setValue(result);
|
|
1071
1364
|
return;
|
|
1072
1365
|
}
|
|
1073
|
-
const stripped =
|
|
1366
|
+
const stripped = Object.fromEntries(keys.filter((k) => k in result).map((k) => [k, result[k]]));
|
|
1074
1367
|
field.setValue(stripped);
|
|
1075
1368
|
};
|
|
1076
1369
|
const onCreate = () => {
|
|
@@ -1081,6 +1374,7 @@ const _sfc_main$c = /* @__PURE__ */ vue.defineComponent({
|
|
|
1081
1374
|
schema: form.json_schema,
|
|
1082
1375
|
uiSchema: form.ui_schema,
|
|
1083
1376
|
modalTitle: `Create new ${wrapper.value.label}`,
|
|
1377
|
+
http,
|
|
1084
1378
|
onClose: (result) => {
|
|
1085
1379
|
if (!result || !result.valid) return;
|
|
1086
1380
|
form.create(result.data).then((res) => {
|
|
@@ -1098,12 +1392,12 @@ const _sfc_main$c = /* @__PURE__ */ vue.defineComponent({
|
|
|
1098
1392
|
});
|
|
1099
1393
|
};
|
|
1100
1394
|
return (_ctx, _cache) => {
|
|
1101
|
-
return
|
|
1395
|
+
return fetchOptions.value ? (vue.openBlock(), vue.createBlock(vue.unref(ui.Autocomplete), vue.mergeProps({ key: 0 }, vue.unref(wrapper), {
|
|
1102
1396
|
"model-value": vue.unref(value),
|
|
1103
|
-
"fetch-options":
|
|
1104
|
-
"label-key":
|
|
1105
|
-
"value-key":
|
|
1106
|
-
"enable-create":
|
|
1397
|
+
"fetch-options": fetchOptions.value.fetchOptions,
|
|
1398
|
+
"label-key": fetchOptions.value.labelKey,
|
|
1399
|
+
"value-key": fetchOptions.value.valueKey,
|
|
1400
|
+
"enable-create": fetchOptions.value.enableCreate,
|
|
1107
1401
|
onChange,
|
|
1108
1402
|
onBlur: vue.unref(onBlur),
|
|
1109
1403
|
onCreate
|
|
@@ -1111,7 +1405,7 @@ const _sfc_main$c = /* @__PURE__ */ vue.defineComponent({
|
|
|
1111
1405
|
};
|
|
1112
1406
|
}
|
|
1113
1407
|
});
|
|
1114
|
-
const _sfc_main$
|
|
1408
|
+
const _sfc_main$g = /* @__PURE__ */ vue.defineComponent({
|
|
1115
1409
|
__name: "BooleanControlRenderer",
|
|
1116
1410
|
props: {
|
|
1117
1411
|
uischema: {},
|
|
@@ -1138,7 +1432,7 @@ const _sfc_main$b = /* @__PURE__ */ vue.defineComponent({
|
|
|
1138
1432
|
};
|
|
1139
1433
|
}
|
|
1140
1434
|
});
|
|
1141
|
-
const _sfc_main$
|
|
1435
|
+
const _sfc_main$f = /* @__PURE__ */ vue.defineComponent({
|
|
1142
1436
|
__name: "MarkdownControlRenderer",
|
|
1143
1437
|
props: {
|
|
1144
1438
|
uischema: {},
|
|
@@ -1166,7 +1460,7 @@ const _sfc_main$a = /* @__PURE__ */ vue.defineComponent({
|
|
|
1166
1460
|
};
|
|
1167
1461
|
}
|
|
1168
1462
|
});
|
|
1169
|
-
const _sfc_main$
|
|
1463
|
+
const _sfc_main$e = /* @__PURE__ */ vue.defineComponent({
|
|
1170
1464
|
__name: "MultiSelectControlRenderer",
|
|
1171
1465
|
props: {
|
|
1172
1466
|
uischema: {},
|
|
@@ -1179,10 +1473,14 @@ const _sfc_main$9 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1179
1473
|
value,
|
|
1180
1474
|
field,
|
|
1181
1475
|
onBlur,
|
|
1182
|
-
onChange: onFieldChange
|
|
1476
|
+
onChange: onFieldChange,
|
|
1477
|
+
appliedOptions
|
|
1183
1478
|
} = useSelectBinding(props.uischema, props.schema);
|
|
1184
1479
|
const onChange = (val) => {
|
|
1185
|
-
|
|
1480
|
+
const opts = appliedOptions.value;
|
|
1481
|
+
const valueKey = opts.valueKey ?? "value";
|
|
1482
|
+
const stored = opts.storeValue && Array.isArray(val) ? val.map((item) => item && typeof item === "object" ? item[valueKey] : item) : val;
|
|
1483
|
+
field.setValue(stored);
|
|
1186
1484
|
onFieldChange();
|
|
1187
1485
|
};
|
|
1188
1486
|
return (_ctx, _cache) => {
|
|
@@ -1194,7 +1492,7 @@ const _sfc_main$9 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1194
1492
|
};
|
|
1195
1493
|
}
|
|
1196
1494
|
});
|
|
1197
|
-
const _sfc_main$
|
|
1495
|
+
const _sfc_main$d = /* @__PURE__ */ vue.defineComponent({
|
|
1198
1496
|
__name: "NumberControlRenderer",
|
|
1199
1497
|
props: {
|
|
1200
1498
|
uischema: {},
|
|
@@ -1218,7 +1516,7 @@ const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1218
1516
|
};
|
|
1219
1517
|
}
|
|
1220
1518
|
});
|
|
1221
|
-
const _sfc_main$
|
|
1519
|
+
const _sfc_main$c = /* @__PURE__ */ vue.defineComponent({
|
|
1222
1520
|
__name: "SelectControlRenderer",
|
|
1223
1521
|
props: {
|
|
1224
1522
|
uischema: {},
|
|
@@ -1234,24 +1532,60 @@ const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1234
1532
|
onChange: onFieldChange,
|
|
1235
1533
|
appliedOptions
|
|
1236
1534
|
} = useSelectBinding(props.uischema, props.schema);
|
|
1535
|
+
const http = useHttpClient();
|
|
1536
|
+
const { values: formValues } = veeValidate.useFormContext();
|
|
1537
|
+
const valueKey = vue.computed(
|
|
1538
|
+
() => appliedOptions.value.valueKey ?? "value"
|
|
1539
|
+
);
|
|
1540
|
+
const isRemote = vue.computed(
|
|
1541
|
+
() => !!appliedOptions.value.uri || !!appliedOptions.value.resource
|
|
1542
|
+
);
|
|
1543
|
+
const remoteOptions = vue.ref([]);
|
|
1544
|
+
vue.watch(
|
|
1545
|
+
[appliedOptions, formValues],
|
|
1546
|
+
async ([opts]) => {
|
|
1547
|
+
if (!isRemote.value) return;
|
|
1548
|
+
const fetcher = await useFetchOptions(
|
|
1549
|
+
opts,
|
|
1550
|
+
http,
|
|
1551
|
+
formValues
|
|
1552
|
+
);
|
|
1553
|
+
if (!fetcher.fetchOptions) return;
|
|
1554
|
+
try {
|
|
1555
|
+
const results = await fetcher.fetchOptions(
|
|
1556
|
+
"",
|
|
1557
|
+
new AbortController().signal
|
|
1558
|
+
);
|
|
1559
|
+
remoteOptions.value = Array.isArray(results) ? results : [];
|
|
1560
|
+
} catch {
|
|
1561
|
+
remoteOptions.value = [];
|
|
1562
|
+
}
|
|
1563
|
+
},
|
|
1564
|
+
{ immediate: true, deep: true }
|
|
1565
|
+
);
|
|
1237
1566
|
const selectOptions = vue.computed(() => {
|
|
1238
|
-
|
|
1567
|
+
if (isRemote.value) return remoteOptions.value;
|
|
1568
|
+
const opts = appliedOptions.value;
|
|
1569
|
+
return opts.options ?? opts.values ?? [];
|
|
1239
1570
|
});
|
|
1240
1571
|
const onChange = (val) => {
|
|
1241
|
-
|
|
1572
|
+
const opts = appliedOptions.value;
|
|
1573
|
+
const stored = opts.storeValue && val && typeof val === "object" ? val[valueKey.value] : val;
|
|
1574
|
+
field.setValue(stored);
|
|
1242
1575
|
onFieldChange();
|
|
1243
1576
|
};
|
|
1244
1577
|
return (_ctx, _cache) => {
|
|
1245
1578
|
return vue.openBlock(), vue.createBlock(vue.unref(ui.SelectComponent), vue.mergeProps(vue.unref(wrapper), {
|
|
1246
1579
|
"model-value": vue.unref(value),
|
|
1247
1580
|
options: selectOptions.value,
|
|
1581
|
+
clearable: vue.unref(appliedOptions).clearable ?? true,
|
|
1248
1582
|
onChange,
|
|
1249
1583
|
onBlur: vue.unref(onBlur)
|
|
1250
|
-
}), null, 16, ["model-value", "options", "onBlur"]);
|
|
1584
|
+
}), null, 16, ["model-value", "options", "clearable", "onBlur"]);
|
|
1251
1585
|
};
|
|
1252
1586
|
}
|
|
1253
1587
|
});
|
|
1254
|
-
const _sfc_main$
|
|
1588
|
+
const _sfc_main$b = /* @__PURE__ */ vue.defineComponent({
|
|
1255
1589
|
__name: "StringControlRenderer",
|
|
1256
1590
|
props: {
|
|
1257
1591
|
uischema: {},
|
|
@@ -1273,7 +1607,7 @@ const _sfc_main$6 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1273
1607
|
};
|
|
1274
1608
|
}
|
|
1275
1609
|
});
|
|
1276
|
-
const _sfc_main$
|
|
1610
|
+
const _sfc_main$a = /* @__PURE__ */ vue.defineComponent({
|
|
1277
1611
|
__name: "TextAreaControlRenderer",
|
|
1278
1612
|
props: {
|
|
1279
1613
|
uischema: {},
|
|
@@ -1281,47 +1615,87 @@ const _sfc_main$5 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1281
1615
|
},
|
|
1282
1616
|
setup(__props) {
|
|
1283
1617
|
const props = __props;
|
|
1284
|
-
const { wrapper, value, onBlur, onChange } = useControlBinding(
|
|
1618
|
+
const { wrapper, value, onBlur, onChange, appliedOptions } = useControlBinding(
|
|
1285
1619
|
props.uischema,
|
|
1286
1620
|
props.schema
|
|
1287
1621
|
);
|
|
1622
|
+
const pathPrefix = vue.inject("pathPrefix", "");
|
|
1623
|
+
const opts = props.uischema.options ?? {};
|
|
1624
|
+
const { values: formValues } = veeValidate.useFormContext();
|
|
1625
|
+
const dir = vue.computed(() => {
|
|
1626
|
+
if (!opts.directionField) return opts.direction ?? "ltr";
|
|
1627
|
+
const key = pathPrefix ? `${pathPrefix}.${opts.directionField}` : opts.directionField;
|
|
1628
|
+
const val = key.split(".").reduce((o, k) => o?.[k], formValues);
|
|
1629
|
+
return val ?? "ltr";
|
|
1630
|
+
});
|
|
1288
1631
|
return (_ctx, _cache) => {
|
|
1289
1632
|
return vue.openBlock(), vue.createBlock(vue.unref(ui.Textarea), vue.mergeProps(vue.unref(wrapper), {
|
|
1290
1633
|
modelValue: vue.unref(value),
|
|
1291
1634
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.isRef(value) ? value.value = $event : null),
|
|
1635
|
+
dir: dir.value,
|
|
1636
|
+
height: vue.unref(appliedOptions).height,
|
|
1637
|
+
"min-height": vue.unref(appliedOptions).minHeight,
|
|
1292
1638
|
onBlur: vue.unref(onBlur),
|
|
1293
1639
|
onChange: vue.unref(onChange)
|
|
1294
|
-
}), null, 16, ["modelValue", "onBlur", "onChange"]);
|
|
1640
|
+
}), null, 16, ["modelValue", "dir", "height", "min-height", "onBlur", "onChange"]);
|
|
1295
1641
|
};
|
|
1296
1642
|
}
|
|
1297
1643
|
});
|
|
1298
|
-
const
|
|
1299
|
-
{ tester:
|
|
1644
|
+
const controlRenderers = [
|
|
1645
|
+
{ tester: rankWith(10, isStringFormat), renderer: _sfc_main$b },
|
|
1300
1646
|
{
|
|
1301
|
-
tester:
|
|
1302
|
-
renderer: _sfc_main$
|
|
1647
|
+
tester: rankWith(11, isTextAreaControl),
|
|
1648
|
+
renderer: _sfc_main$a
|
|
1303
1649
|
},
|
|
1304
1650
|
{
|
|
1305
|
-
tester:
|
|
1306
|
-
renderer: _sfc_main$
|
|
1651
|
+
tester: rankWith(11, isMarkdownControl),
|
|
1652
|
+
renderer: _sfc_main$f
|
|
1307
1653
|
},
|
|
1308
|
-
{ tester:
|
|
1309
|
-
{ tester:
|
|
1654
|
+
{ tester: rankWith(11, isBooleanControl), renderer: _sfc_main$g },
|
|
1655
|
+
{ tester: rankWith(11, isSelectControl), renderer: _sfc_main$c },
|
|
1310
1656
|
{
|
|
1311
|
-
tester:
|
|
1312
|
-
renderer: _sfc_main$
|
|
1657
|
+
tester: rankWith(11, isMultiselectControl),
|
|
1658
|
+
renderer: _sfc_main$e
|
|
1313
1659
|
},
|
|
1314
1660
|
{
|
|
1315
|
-
tester:
|
|
1316
|
-
renderer: _sfc_main$
|
|
1661
|
+
tester: rankWith(12, isAutoCompleteControl),
|
|
1662
|
+
renderer: _sfc_main$h
|
|
1317
1663
|
},
|
|
1318
1664
|
{
|
|
1319
|
-
tester:
|
|
1320
|
-
renderer: _sfc_main$
|
|
1665
|
+
tester: rankWith(12, isNumberFormat),
|
|
1666
|
+
renderer: _sfc_main$d
|
|
1321
1667
|
},
|
|
1322
1668
|
{
|
|
1323
|
-
tester:
|
|
1324
|
-
renderer: _sfc_main$
|
|
1669
|
+
tester: rankWith(12, isIntegerFormat),
|
|
1670
|
+
renderer: _sfc_main$d
|
|
1671
|
+
}
|
|
1672
|
+
];
|
|
1673
|
+
const _sfc_main$9 = /* @__PURE__ */ vue.defineComponent({
|
|
1674
|
+
__name: "ControlReadonlyRenderer",
|
|
1675
|
+
props: {
|
|
1676
|
+
uischema: {},
|
|
1677
|
+
schema: {}
|
|
1678
|
+
},
|
|
1679
|
+
setup(__props) {
|
|
1680
|
+
const props = __props;
|
|
1681
|
+
const { wrapper, displayWrapper } = useReadonlyControlBinding(
|
|
1682
|
+
props.uischema,
|
|
1683
|
+
props.schema
|
|
1684
|
+
);
|
|
1685
|
+
return (_ctx, _cache) => {
|
|
1686
|
+
return vue.openBlock(), vue.createBlock(_sfc_main$u, vue.normalizeProps(vue.guardReactiveProps(vue.unref(wrapper))), {
|
|
1687
|
+
default: vue.withCtx(() => [
|
|
1688
|
+
(vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(vue.unref(displayWrapper).component), vue.normalizeProps(vue.guardReactiveProps(vue.unref(displayWrapper).value)), null, 16))
|
|
1689
|
+
]),
|
|
1690
|
+
_: 1
|
|
1691
|
+
}, 16);
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
const readonlyControlRenderers = [
|
|
1696
|
+
{
|
|
1697
|
+
tester: rankWith(10, uiTypeIs("Control")),
|
|
1698
|
+
renderer: _sfc_main$9
|
|
1325
1699
|
}
|
|
1326
1700
|
];
|
|
1327
1701
|
const COLSPAN = {
|
|
@@ -1338,8 +1712,8 @@ const COLSPAN = {
|
|
|
1338
1712
|
11: "col-span-11",
|
|
1339
1713
|
12: "col-span-12"
|
|
1340
1714
|
};
|
|
1341
|
-
const _hoisted_1$
|
|
1342
|
-
const _sfc_main$
|
|
1715
|
+
const _hoisted_1$7 = { class: "flex flex-col gap-4" };
|
|
1716
|
+
const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
|
|
1343
1717
|
__name: "CollapseLayoutRenderer",
|
|
1344
1718
|
props: {
|
|
1345
1719
|
uischema: {},
|
|
@@ -1359,13 +1733,13 @@ const _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1359
1733
|
return (_ctx, _cache) => {
|
|
1360
1734
|
return vue.openBlock(), vue.createBlock(vue.unref(ui.Collapse), { title: title.value }, {
|
|
1361
1735
|
default: vue.withCtx(() => [
|
|
1362
|
-
vue.createElementVNode("div", _hoisted_1$
|
|
1736
|
+
vue.createElementVNode("div", _hoisted_1$7, [
|
|
1363
1737
|
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.uischema.elements, (child, i) => {
|
|
1364
1738
|
return vue.openBlock(), vue.createElementBlock("div", {
|
|
1365
1739
|
key: i,
|
|
1366
1740
|
class: vue.normalizeClass(vue.unref(COLSPAN)[child.options?.colspan ?? 12])
|
|
1367
1741
|
}, [
|
|
1368
|
-
vue.createVNode(_sfc_main$
|
|
1742
|
+
vue.createVNode(_sfc_main$5, {
|
|
1369
1743
|
uischema: child,
|
|
1370
1744
|
schema: __props.schema
|
|
1371
1745
|
}, null, 8, ["uischema", "schema"])
|
|
@@ -1378,11 +1752,11 @@ const _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1378
1752
|
};
|
|
1379
1753
|
}
|
|
1380
1754
|
});
|
|
1381
|
-
const _hoisted_1$
|
|
1755
|
+
const _hoisted_1$6 = {
|
|
1382
1756
|
key: 1,
|
|
1383
1757
|
class: "flex flex-col gap-3"
|
|
1384
1758
|
};
|
|
1385
|
-
const _sfc_main$
|
|
1759
|
+
const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
|
|
1386
1760
|
__name: "LayoutRenderer",
|
|
1387
1761
|
props: {
|
|
1388
1762
|
uischema: {},
|
|
@@ -1391,8 +1765,11 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1391
1765
|
setup(__props) {
|
|
1392
1766
|
const props = __props;
|
|
1393
1767
|
const LAYOUT = {
|
|
1394
|
-
|
|
1395
|
-
|
|
1768
|
+
// Stack on narrow viewports (e.g. small modals), switch to the 12-column
|
|
1769
|
+
// grid at md+. Children keep their `col-span-*`; with a single column the
|
|
1770
|
+
// span clamps to full width, so fields stack cleanly.
|
|
1771
|
+
GridLayout: "grid grid-cols-1 gap-x-3 md:grid-cols-12",
|
|
1772
|
+
HorizontalLayout: "flex flex-col gap-y-3 md:flex-row",
|
|
1396
1773
|
VerticalLayout: "flex flex-col gap-3"
|
|
1397
1774
|
};
|
|
1398
1775
|
const getLayout = vue.computed(() => LAYOUT[props.uischema.type]);
|
|
@@ -1407,31 +1784,175 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1407
1784
|
key: i,
|
|
1408
1785
|
class: vue.normalizeClass(vue.unref(COLSPAN)[child.options?.colspan ?? 12])
|
|
1409
1786
|
}, [
|
|
1410
|
-
vue.createVNode(_sfc_main$
|
|
1787
|
+
vue.createVNode(_sfc_main$5, {
|
|
1411
1788
|
uischema: child,
|
|
1412
1789
|
schema: __props.schema
|
|
1413
1790
|
}, null, 8, ["uischema", "schema"])
|
|
1414
1791
|
], 2);
|
|
1415
1792
|
}), 128))
|
|
1416
|
-
], 2)) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$
|
|
1793
|
+
], 2)) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$6, " No Applicable Layout found "));
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
const _hoisted_1$5 = {
|
|
1798
|
+
class: "grid items-center overflow-hidden",
|
|
1799
|
+
style: { "grid-template-columns": "minmax(150px, auto) minmax(0, 1fr)" }
|
|
1800
|
+
};
|
|
1801
|
+
const _sfc_main$6 = /* @__PURE__ */ vue.defineComponent({
|
|
1802
|
+
__name: "ReadOnlyLayoutRenderer",
|
|
1803
|
+
props: {
|
|
1804
|
+
uischema: {},
|
|
1805
|
+
schema: {}
|
|
1806
|
+
},
|
|
1807
|
+
setup(__props) {
|
|
1808
|
+
return (_ctx, _cache) => {
|
|
1809
|
+
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$5, [
|
|
1810
|
+
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.uischema.elements, (child, i) => {
|
|
1811
|
+
return vue.openBlock(), vue.createBlock(_sfc_main$5, {
|
|
1812
|
+
key: i,
|
|
1813
|
+
uischema: child,
|
|
1814
|
+
schema: __props.schema
|
|
1815
|
+
}, null, 8, ["uischema", "schema"]);
|
|
1816
|
+
}), 128))
|
|
1817
|
+
]);
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
});
|
|
1821
|
+
const isLayoutType = or(
|
|
1822
|
+
uiTypeIs("GridLayout"),
|
|
1823
|
+
uiTypeIs("HorizontalLayout"),
|
|
1824
|
+
uiTypeIs("VerticalLayout")
|
|
1825
|
+
);
|
|
1826
|
+
const layoutRenderers = [
|
|
1827
|
+
{ tester: rankWith(10, isLayoutType), renderer: _sfc_main$7 },
|
|
1828
|
+
{
|
|
1829
|
+
tester: rankWith(10, uiTypeIs("CollapseLayout")),
|
|
1830
|
+
renderer: _sfc_main$8
|
|
1831
|
+
}
|
|
1832
|
+
];
|
|
1833
|
+
const readonlyLayoutRenderers = [
|
|
1834
|
+
{ tester: rankWith(10, isLayoutType), renderer: _sfc_main$6 },
|
|
1835
|
+
{
|
|
1836
|
+
tester: rankWith(10, uiTypeIs("CollapseLayout")),
|
|
1837
|
+
renderer: _sfc_main$8
|
|
1838
|
+
}
|
|
1839
|
+
];
|
|
1840
|
+
const customRenderers = [
|
|
1841
|
+
layoutRenderers,
|
|
1842
|
+
controlRenderers,
|
|
1843
|
+
arrayRenderers
|
|
1844
|
+
].flat();
|
|
1845
|
+
const readonlyRenderers = [
|
|
1846
|
+
readonlyLayoutRenderers,
|
|
1847
|
+
readonlyControlRenderers,
|
|
1848
|
+
arrayRenderers
|
|
1849
|
+
].flat();
|
|
1850
|
+
function findRenderer(registry, uischema, schema) {
|
|
1851
|
+
let best = null;
|
|
1852
|
+
for (const entry of registry) {
|
|
1853
|
+
const rank = entry.tester(uischema, schema);
|
|
1854
|
+
if (rank > -1 && (!best || rank > best.rank)) {
|
|
1855
|
+
best = { rank, renderer: entry.renderer };
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
return best?.renderer ?? null;
|
|
1859
|
+
}
|
|
1860
|
+
const _hoisted_1$4 = {
|
|
1861
|
+
key: 1,
|
|
1862
|
+
class: "text-error text-xs"
|
|
1863
|
+
};
|
|
1864
|
+
const _sfc_main$5 = /* @__PURE__ */ vue.defineComponent({
|
|
1865
|
+
__name: "Dispatch",
|
|
1866
|
+
props: {
|
|
1867
|
+
uischema: {},
|
|
1868
|
+
schema: {},
|
|
1869
|
+
pathPrefix: { default: void 0 }
|
|
1870
|
+
},
|
|
1871
|
+
setup(__props) {
|
|
1872
|
+
const props = __props;
|
|
1873
|
+
const editableRegistry = vue.inject("renderers");
|
|
1874
|
+
const extraReadonlyRenderers = vue.inject("readonlyRenderers", []);
|
|
1875
|
+
const formReadonly = vue.inject(FORM_READONLY_KEY, vue.ref(false));
|
|
1876
|
+
const effectiveReadonlyRenderers = vue.computed(
|
|
1877
|
+
() => extraReadonlyRenderers.length ? [...readonlyRenderers, ...extraReadonlyRenderers] : readonlyRenderers
|
|
1878
|
+
);
|
|
1879
|
+
const registry = vue.computed(
|
|
1880
|
+
() => formReadonly.value ? effectiveReadonlyRenderers.value : editableRegistry
|
|
1881
|
+
);
|
|
1882
|
+
const rootSchema = vue.inject("rootSchema");
|
|
1883
|
+
const parentPrefix = vue.inject("pathPrefix", "");
|
|
1884
|
+
const effectivePrefix = props.pathPrefix ?? parentPrefix;
|
|
1885
|
+
if (props.pathPrefix !== void 0) {
|
|
1886
|
+
vue.provide("pathPrefix", effectivePrefix);
|
|
1887
|
+
}
|
|
1888
|
+
const resolved = vue.computed(() => {
|
|
1889
|
+
const u = props.uischema;
|
|
1890
|
+
if (!u.scope) return props.schema;
|
|
1891
|
+
const fromRoot = resolveSchema(rootSchema, u.scope);
|
|
1892
|
+
if (fromRoot) return fromRoot;
|
|
1893
|
+
return resolveSchema(props.schema, u.scope) ?? props.schema;
|
|
1894
|
+
});
|
|
1895
|
+
const renderer = vue.computed(
|
|
1896
|
+
() => findRenderer(
|
|
1897
|
+
registry.value,
|
|
1898
|
+
props.uischema,
|
|
1899
|
+
resolved.value
|
|
1900
|
+
)
|
|
1901
|
+
);
|
|
1902
|
+
return (_ctx, _cache) => {
|
|
1903
|
+
return renderer.value ? (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(renderer.value), {
|
|
1904
|
+
key: 0,
|
|
1905
|
+
uischema: __props.uischema,
|
|
1906
|
+
schema: resolved.value
|
|
1907
|
+
}, null, 8, ["uischema", "schema"])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$4, " No renderer for " + vue.toDisplayString(__props.uischema.scope) + " type: " + vue.toDisplayString(__props.uischema.type), 1));
|
|
1417
1908
|
};
|
|
1418
1909
|
}
|
|
1419
1910
|
});
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
{
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1911
|
+
const JsonFormComponentProperties = {
|
|
1912
|
+
/** Unique identifier used to namespace the form element. */
|
|
1913
|
+
id: { type: String, required: true },
|
|
1914
|
+
/** HTML name attribute for the form. */
|
|
1915
|
+
name: { type: String, default: "form" },
|
|
1916
|
+
/** JSON schema describing the shape of the form data. */
|
|
1917
|
+
schema: { type: Object, required: true },
|
|
1918
|
+
/** UI schema describing the layout and controls. */
|
|
1919
|
+
uiSchema: { type: Object, required: true },
|
|
1920
|
+
/** Disable all form controls. */
|
|
1921
|
+
disabled: { type: Boolean, default: false },
|
|
1922
|
+
/** Make all form controls readonly. */
|
|
1923
|
+
readonly: { type: Boolean, default: false },
|
|
1924
|
+
/** Current form data object. */
|
|
1925
|
+
formData: { type: Object, default: () => ({}) },
|
|
1926
|
+
/** When validation errors are shown (`'onBlur'`, `'onChange'`, `'onSubmit'`, `'always'`). */
|
|
1927
|
+
errorMode: {
|
|
1928
|
+
type: String,
|
|
1929
|
+
default: "onChanges"
|
|
1930
|
+
},
|
|
1931
|
+
/** HTTP client used by renderers that fetch remote data (e.g. autocomplete). */
|
|
1932
|
+
http: {
|
|
1933
|
+
type: Object,
|
|
1934
|
+
default: null
|
|
1935
|
+
},
|
|
1936
|
+
/** Custom renderer registry. Overrides the default `customRenderers` when provided. */
|
|
1937
|
+
renderers: {
|
|
1938
|
+
type: Array,
|
|
1939
|
+
default: null
|
|
1430
1940
|
}
|
|
1941
|
+
};
|
|
1942
|
+
const JsonFormComponentEmits = [
|
|
1943
|
+
/** Emitted when form data changes. */
|
|
1944
|
+
"change",
|
|
1945
|
+
/** Emitted on form submission. */
|
|
1946
|
+
"submit",
|
|
1947
|
+
/** Emitted when validation errors change. */
|
|
1948
|
+
"errors",
|
|
1949
|
+
/** Emitted when form validity changes. */
|
|
1950
|
+
"valid",
|
|
1951
|
+
/** Emitted when a custom renderer dispatches a form event. */
|
|
1952
|
+
"events"
|
|
1431
1953
|
];
|
|
1432
|
-
const
|
|
1433
|
-
const
|
|
1434
|
-
const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
|
|
1954
|
+
const _hoisted_1$3 = ["id"];
|
|
1955
|
+
const _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
|
|
1435
1956
|
__name: "FormComponent",
|
|
1436
1957
|
props: JsonFormComponentProperties,
|
|
1437
1958
|
emits: JsonFormComponentEmits,
|
|
@@ -1452,12 +1973,17 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1452
1973
|
validationSchema: zodSchema,
|
|
1453
1974
|
initialValues: properties.formData
|
|
1454
1975
|
});
|
|
1455
|
-
vue.provide(
|
|
1976
|
+
vue.provide(
|
|
1977
|
+
"renderers",
|
|
1978
|
+
properties.renderers?.length ? [...customRenderers, ...properties.renderers] : customRenderers
|
|
1979
|
+
);
|
|
1980
|
+
vue.provide("readonlyRenderers", properties.renderers ?? []);
|
|
1456
1981
|
vue.provide("rootSchema", properties.schema);
|
|
1457
1982
|
vue.provide("styles", ui.myStyles);
|
|
1458
1983
|
const submitted = vue.ref(false);
|
|
1459
1984
|
vue.provide(ERROR_MODE_KEY, vue.toRef(properties, "errorMode"));
|
|
1460
1985
|
vue.provide(FORM_SUBMITTED_KEY, submitted);
|
|
1986
|
+
vue.provide(FORM_READONLY_KEY, vue.toRef(properties, "readonly"));
|
|
1461
1987
|
vue.onMounted(async () => {
|
|
1462
1988
|
const result = await validate();
|
|
1463
1989
|
emits("valid", result.valid);
|
|
@@ -1465,6 +1991,9 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1465
1991
|
provideFormEvents((payload) => {
|
|
1466
1992
|
emits("events", payload);
|
|
1467
1993
|
});
|
|
1994
|
+
if (properties.http) {
|
|
1995
|
+
provideHttpClient(properties.http);
|
|
1996
|
+
}
|
|
1468
1997
|
let syncing = false;
|
|
1469
1998
|
vue.watch(
|
|
1470
1999
|
() => properties.formData,
|
|
@@ -1513,380 +2042,476 @@ const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
|
|
|
1513
2042
|
id: _ctx.id,
|
|
1514
2043
|
onSubmit: vue.withModifiers(onSubmit, ["prevent"])
|
|
1515
2044
|
}, [
|
|
1516
|
-
vue.createVNode(_sfc_main$
|
|
2045
|
+
vue.createVNode(_sfc_main$5, {
|
|
1517
2046
|
uischema: _ctx.uiSchema,
|
|
1518
2047
|
schema: _ctx.schema
|
|
1519
2048
|
}, null, 8, ["uischema", "schema"])
|
|
1520
|
-
], 40, _hoisted_1$
|
|
2049
|
+
], 40, _hoisted_1$3);
|
|
1521
2050
|
};
|
|
1522
2051
|
}
|
|
1523
2052
|
});
|
|
1524
|
-
const
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
errorMode: {
|
|
1545
|
-
type: String,
|
|
1546
|
-
default: "onBlur"
|
|
1547
|
-
}
|
|
1548
|
-
};
|
|
1549
|
-
const FormWithActionsEmits = [
|
|
1550
|
-
/** Emitted when `modelValue` changes. */
|
|
1551
|
-
"update:modelValue",
|
|
1552
|
-
/** Emitted after a successful `FormStore.save()`. */
|
|
1553
|
-
"success",
|
|
1554
|
-
/** Emitted on submit when no `uri` is provided. */
|
|
1555
|
-
"submit",
|
|
1556
|
-
/** Emitted whenever form validity changes. */
|
|
1557
|
-
"valid",
|
|
1558
|
-
/** Emitted when a custom renderer dispatches a form event. */
|
|
1559
|
-
"events",
|
|
1560
|
-
/** Emitted when validation errors change. */
|
|
1561
|
-
"errors",
|
|
1562
|
-
/** Emitted when the user cancels editing an existing record. */
|
|
1563
|
-
"cancel"
|
|
1564
|
-
];
|
|
1565
|
-
class FormStore {
|
|
1566
|
-
constructor(uri) {
|
|
1567
|
-
this.uri = uri;
|
|
1568
|
-
}
|
|
1569
|
-
async delete(data) {
|
|
1570
|
-
return toolsVue.useApi().delete(`${this.uri}/${data.id}`).then(() => {
|
|
1571
|
-
ui.NotificationService.success("Data deleted");
|
|
1572
|
-
}).catch((error) => {
|
|
1573
|
-
console.error(error);
|
|
1574
|
-
ui.NotificationService.error("Error deleting data");
|
|
2053
|
+
const createRepository = (formSchemaModel, httpRequest, options = {}) => {
|
|
2054
|
+
const notificationEntity = options.notification?.entityType || "entity";
|
|
2055
|
+
const notificationStore = options.notification?.notification ?? null;
|
|
2056
|
+
const getDataUri = (...suffix) => {
|
|
2057
|
+
return [formSchemaModel.uri, ...suffix].join("/");
|
|
2058
|
+
};
|
|
2059
|
+
const handleSuccess = (message) => {
|
|
2060
|
+
notificationStore?.success(message);
|
|
2061
|
+
};
|
|
2062
|
+
const handleError = (error, message) => {
|
|
2063
|
+
console.error(error);
|
|
2064
|
+
notificationStore?.error(message);
|
|
2065
|
+
throw new Error(error);
|
|
2066
|
+
};
|
|
2067
|
+
const create = (object, options2) => {
|
|
2068
|
+
return httpRequest.post(getDataUri(), object, options2).then((response) => {
|
|
2069
|
+
handleSuccess(`Created ${notificationEntity}`);
|
|
2070
|
+
return response.data;
|
|
2071
|
+
}).catch((response) => {
|
|
2072
|
+
handleError(response, `Failed to create ${notificationEntity}`);
|
|
1575
2073
|
});
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
2074
|
+
};
|
|
2075
|
+
const patch = (id, object, options2) => {
|
|
2076
|
+
return httpRequest.patch(getDataUri(id), object, options2).then((response) => {
|
|
2077
|
+
handleSuccess(`Saved ${notificationEntity}`);
|
|
2078
|
+
return response.data;
|
|
2079
|
+
}).catch((response) => {
|
|
2080
|
+
handleError(response, `Failed to save ${notificationEntity}`);
|
|
2081
|
+
});
|
|
2082
|
+
};
|
|
2083
|
+
const get = (id, options2) => {
|
|
2084
|
+
return httpRequest.get(getDataUri(id), options2).then((response) => {
|
|
1582
2085
|
return response.data;
|
|
1583
|
-
}).catch((
|
|
1584
|
-
|
|
1585
|
-
|
|
2086
|
+
}).catch((response) => {
|
|
2087
|
+
handleError(response, "Failed to load data");
|
|
2088
|
+
});
|
|
2089
|
+
};
|
|
2090
|
+
const _delete = (id, options2) => {
|
|
2091
|
+
return httpRequest.delete(getDataUri(id), options2).then((response) => {
|
|
2092
|
+
handleSuccess(`${notificationEntity} deleted`);
|
|
2093
|
+
return response;
|
|
2094
|
+
}).catch((response) => {
|
|
2095
|
+
handleError(response, `Failed to delete ${notificationEntity}`);
|
|
2096
|
+
});
|
|
2097
|
+
};
|
|
2098
|
+
const createMulti = (objects, options2) => {
|
|
2099
|
+
return Promise.all(
|
|
2100
|
+
objects.map((object) => httpRequest.post(getDataUri(), object, options2))
|
|
2101
|
+
).then((responses) => {
|
|
2102
|
+
handleSuccess(`Created ${notificationEntity}`);
|
|
2103
|
+
return responses.map((r) => r.data);
|
|
2104
|
+
}).catch((response) => {
|
|
2105
|
+
handleError(response, `Failed to save ${notificationEntity}`);
|
|
1586
2106
|
});
|
|
2107
|
+
};
|
|
2108
|
+
return { create, patch, createMulti, delete: _delete, get };
|
|
2109
|
+
};
|
|
2110
|
+
const TableComponentProperties = {
|
|
2111
|
+
id: { type: String, required: true },
|
|
2112
|
+
uiSchema: { type: Object, required: true },
|
|
2113
|
+
schema: { type: Object, required: true },
|
|
2114
|
+
reload: { type: Number },
|
|
2115
|
+
loading: { type: Boolean, default: false },
|
|
2116
|
+
multiselect: { type: Boolean, default: false },
|
|
2117
|
+
actions: { type: Array },
|
|
2118
|
+
data: { type: Array },
|
|
2119
|
+
page: { type: Object },
|
|
2120
|
+
sort: { type: Object },
|
|
2121
|
+
cellRenderers: { type: Array },
|
|
2122
|
+
hidePagination: { type: Boolean, default: false }
|
|
2123
|
+
};
|
|
2124
|
+
const TableComponentEmits = [
|
|
2125
|
+
"updatePage",
|
|
2126
|
+
"updatePageSize",
|
|
2127
|
+
"sort",
|
|
2128
|
+
"selectionChange"
|
|
2129
|
+
];
|
|
2130
|
+
const cellTypeIs = (type, rank) => (element) => element.type === type ? rank : -1;
|
|
2131
|
+
const cellFormatIs = (format, rank) => (element) => element.options?.format === format ? rank : -1;
|
|
2132
|
+
const findCellRenderer = (registry, element) => {
|
|
2133
|
+
let best;
|
|
2134
|
+
for (const entry of registry) {
|
|
2135
|
+
const rank = entry.tester(element);
|
|
2136
|
+
if (rank > -1 && (!best || rank > best.rank)) {
|
|
2137
|
+
best = { rank, renderer: entry.renderer };
|
|
2138
|
+
}
|
|
1587
2139
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
2140
|
+
return best?.renderer;
|
|
2141
|
+
};
|
|
2142
|
+
const defaultCellRenderers = [
|
|
2143
|
+
{ tester: cellTypeIs("TextCell", 10), renderer: ui.TextCell },
|
|
2144
|
+
{ tester: cellTypeIs("BooleanCell", 10), renderer: ui.BooleanCell }
|
|
2145
|
+
];
|
|
2146
|
+
const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
|
|
2147
|
+
__name: "TableComponent",
|
|
2148
|
+
props: TableComponentProperties,
|
|
2149
|
+
emits: TableComponentEmits,
|
|
1595
2150
|
setup(__props, { emit: __emit }) {
|
|
1596
2151
|
const properties = __props;
|
|
1597
2152
|
const emits = __emit;
|
|
1598
|
-
const
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
2153
|
+
const allRenderers = vue.computed(() => [
|
|
2154
|
+
...properties.cellRenderers ?? [],
|
|
2155
|
+
...defaultCellRenderers
|
|
2156
|
+
]);
|
|
2157
|
+
const displayColumns = vue.computed(
|
|
2158
|
+
() => properties.uiSchema.elements.map((e) => {
|
|
2159
|
+
const element = e;
|
|
2160
|
+
const def = jsonFormsCore.findColumnDef(element, properties.schema);
|
|
2161
|
+
const type = Array.isArray(def.type) ? def.type[0] : def.type;
|
|
2162
|
+
const component = findCellRenderer(allRenderers.value, element);
|
|
2163
|
+
if (!component)
|
|
2164
|
+
console.warn("No cell renderer found for", element.type, element.options?.format);
|
|
2165
|
+
return {
|
|
2166
|
+
...def,
|
|
2167
|
+
label: e.options?.label ?? def.id,
|
|
2168
|
+
type,
|
|
2169
|
+
component
|
|
2170
|
+
};
|
|
2171
|
+
})
|
|
1614
2172
|
);
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
emits("success", response);
|
|
1625
|
-
});
|
|
1626
|
-
} else {
|
|
1627
|
-
emits("submit", formData.value);
|
|
1628
|
-
}
|
|
1629
|
-
};
|
|
1630
|
-
const clear = () => {
|
|
1631
|
-
formData.value = { id: null };
|
|
1632
|
-
emits("update:modelValue", formData.value);
|
|
2173
|
+
return (_ctx, _cache) => {
|
|
2174
|
+
return vue.openBlock(), vue.createBlock(vue.unref(ui.Table), vue.mergeProps(properties, {
|
|
2175
|
+
"display-columns": displayColumns.value,
|
|
2176
|
+
page: _ctx.hidePagination ? void 0 : _ctx.page,
|
|
2177
|
+
onSort: _cache[0] || (_cache[0] = (id) => emits("sort", id)),
|
|
2178
|
+
onUpdatePage: _cache[1] || (_cache[1] = (page) => emits("updatePage", page)),
|
|
2179
|
+
onUpdatePageSize: _cache[2] || (_cache[2] = (size) => emits("updatePageSize", size)),
|
|
2180
|
+
onSelectionChange: _cache[3] || (_cache[3] = (e) => emits("selectionChange", e))
|
|
2181
|
+
}), null, 16, ["display-columns", "page"]);
|
|
1633
2182
|
};
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
const _hoisted_1$2 = { class: "flex gap-2 items-center" };
|
|
2186
|
+
const _hoisted_2$2 = {
|
|
2187
|
+
key: 1,
|
|
2188
|
+
class: "flex-1 min-w-0"
|
|
2189
|
+
};
|
|
2190
|
+
const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
|
|
2191
|
+
__name: "FilterRowInput",
|
|
2192
|
+
props: {
|
|
2193
|
+
modelValue: {},
|
|
2194
|
+
fields: {}
|
|
2195
|
+
},
|
|
2196
|
+
emits: ["update:modelValue", "remove"],
|
|
2197
|
+
setup(__props, { emit: __emit }) {
|
|
2198
|
+
const props = __props;
|
|
2199
|
+
const emit = __emit;
|
|
2200
|
+
const noValue = vue.computed(() => jsonFormsCore.OperatorNoValue.has(props.modelValue.operator));
|
|
2201
|
+
const update = (key, value) => {
|
|
2202
|
+
emit("update:modelValue", { ...props.modelValue, [key]: value });
|
|
1638
2203
|
};
|
|
1639
|
-
const
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
valid.value = lodashEs.isEmpty(errors);
|
|
2204
|
+
const onOperatorChange = (op) => {
|
|
2205
|
+
emit("update:modelValue", {
|
|
2206
|
+
...props.modelValue,
|
|
2207
|
+
operator: op,
|
|
2208
|
+
value: jsonFormsCore.OperatorNoValue.has(op) ? "" : props.modelValue.value
|
|
2209
|
+
});
|
|
1646
2210
|
};
|
|
1647
|
-
vue.watch(valid, (newValid, oldValid) => {
|
|
1648
|
-
if (newValid !== oldValid) {
|
|
1649
|
-
emits("valid", newValid);
|
|
1650
|
-
}
|
|
1651
|
-
});
|
|
1652
2211
|
return (_ctx, _cache) => {
|
|
1653
|
-
return vue.openBlock(), vue.
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
onClick: cancel
|
|
1687
|
-
}, {
|
|
1688
|
-
default: vue.withCtx(() => [..._cache[1] || (_cache[1] = [
|
|
1689
|
-
vue.createTextVNode(" Cancel ", -1)
|
|
1690
|
-
])]),
|
|
1691
|
-
_: 1
|
|
1692
|
-
})) : (vue.openBlock(), vue.createBlock(vue.unref(ui.Btn), {
|
|
1693
|
-
key: 1,
|
|
1694
|
-
"aria-label": "Clear",
|
|
1695
|
-
outline: true,
|
|
1696
|
-
onClick: clear
|
|
1697
|
-
}, {
|
|
1698
|
-
default: vue.withCtx(() => [..._cache[2] || (_cache[2] = [
|
|
1699
|
-
vue.createTextVNode(" Clear ", -1)
|
|
1700
|
-
])]),
|
|
1701
|
-
_: 1
|
|
1702
|
-
})),
|
|
1703
|
-
vue.createVNode(vue.unref(ui.Btn), {
|
|
1704
|
-
"aria-label": "Save",
|
|
1705
|
-
color: vue.unref(ui.Color).primary,
|
|
1706
|
-
disabled: !valid.value,
|
|
1707
|
-
onClick: save
|
|
1708
|
-
}, {
|
|
1709
|
-
default: vue.withCtx(() => [..._cache[3] || (_cache[3] = [
|
|
1710
|
-
vue.createTextVNode(" Save ", -1)
|
|
1711
|
-
])]),
|
|
1712
|
-
_: 1
|
|
1713
|
-
}, 8, ["color", "disabled"])
|
|
1714
|
-
])
|
|
1715
|
-
])
|
|
1716
|
-
], 2)
|
|
1717
|
-
]),
|
|
1718
|
-
_: 3
|
|
1719
|
-
}, 8, ["title", "height-full"]);
|
|
2212
|
+
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$2, [
|
|
2213
|
+
vue.createVNode(vue.unref(ui.SelectComponent), {
|
|
2214
|
+
size: "sm",
|
|
2215
|
+
value: __props.modelValue.key,
|
|
2216
|
+
options: __props.fields,
|
|
2217
|
+
clearable: false,
|
|
2218
|
+
onChange: _cache[0] || (_cache[0] = ($event) => update("key", $event.value))
|
|
2219
|
+
}, null, 8, ["value", "options"]),
|
|
2220
|
+
vue.createVNode(vue.unref(ui.SelectComponent), {
|
|
2221
|
+
size: "sm",
|
|
2222
|
+
value: __props.modelValue.operator,
|
|
2223
|
+
options: vue.unref(jsonFormsCore.OperatorOptions),
|
|
2224
|
+
clearable: false,
|
|
2225
|
+
onChange: _cache[1] || (_cache[1] = ($event) => onOperatorChange($event.value))
|
|
2226
|
+
}, null, 8, ["value", "options"]),
|
|
2227
|
+
!noValue.value ? (vue.openBlock(), vue.createBlock(vue.unref(ui.Input), {
|
|
2228
|
+
key: 0,
|
|
2229
|
+
size: "sm",
|
|
2230
|
+
placeholder: "Enter a value",
|
|
2231
|
+
value: __props.modelValue.value,
|
|
2232
|
+
clearable: true,
|
|
2233
|
+
onInput: _cache[2] || (_cache[2] = ($event) => update("value", $event.target.value))
|
|
2234
|
+
}, null, 8, ["value"])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$2)),
|
|
2235
|
+
vue.createVNode(vue.unref(ui.Btn), {
|
|
2236
|
+
icon: vue.unref(ui.IconEnum).Delete,
|
|
2237
|
+
size: "xs",
|
|
2238
|
+
outline: true,
|
|
2239
|
+
color: "error",
|
|
2240
|
+
"no-border": true,
|
|
2241
|
+
tooltip: "Remove filter",
|
|
2242
|
+
onClick: _cache[3] || (_cache[3] = ($event) => _ctx.$emit("remove"))
|
|
2243
|
+
}, null, 8, ["icon"])
|
|
2244
|
+
]);
|
|
1720
2245
|
};
|
|
1721
2246
|
}
|
|
1722
2247
|
});
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
tableTitle: { type: String, required: true },
|
|
1728
|
-
/** Title shown in the modal when creating a new record. */
|
|
1729
|
-
createTitle: { type: String, required: true },
|
|
1730
|
-
/** Title shown in the modal when editing a record. Falls back to `createTitle` when omitted. */
|
|
1731
|
-
updateTitle: { type: String },
|
|
1732
|
-
/** Override URI used to fetch table data. Defaults to `uri` when omitted. */
|
|
1733
|
-
dataUri: { type: String },
|
|
1734
|
-
/** Custom row actions rendered in the table. */
|
|
1735
|
-
tableActions: { type: Array },
|
|
1736
|
-
/** JSON Forms layout for the create/edit modal form. */
|
|
1737
|
-
form: { type: Object },
|
|
1738
|
-
/** JSON Forms layout for the table. */
|
|
1739
|
-
table: { type: Object },
|
|
1740
|
-
/** JSON Forms layout for the table filter. */
|
|
1741
|
-
filter: { type: Object },
|
|
1742
|
-
/** REST endpoint used by `FormStore` for CRUD operations. */
|
|
1743
|
-
uri: { type: String },
|
|
1744
|
-
/** Default data pre-filled when creating a new record. */
|
|
1745
|
-
initialData: { type: Object, default: () => ({}) },
|
|
1746
|
-
/** When validation errors are shown in the modal form. */
|
|
1747
|
-
errorMode: {
|
|
1748
|
-
type: String,
|
|
1749
|
-
default: "onBlur"
|
|
1750
|
-
}
|
|
2248
|
+
const _hoisted_1$1 = { class: "px-2 flex gap-2 items-center" };
|
|
2249
|
+
const _hoisted_2$1 = {
|
|
2250
|
+
key: 0,
|
|
2251
|
+
class: "badge badge-sm bg-base-300 text-base-700"
|
|
1751
2252
|
};
|
|
1752
|
-
const
|
|
1753
|
-
|
|
1754
|
-
"
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
"
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
]
|
|
1766
|
-
const _hoisted_1 = { class: "flex justify-between items-center mb-2" };
|
|
1767
|
-
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
1768
|
-
__name: "FormWithTable",
|
|
1769
|
-
props: FormWithTableProperties,
|
|
1770
|
-
emits: FormWithTableEmits,
|
|
2253
|
+
const _hoisted_3$1 = {
|
|
2254
|
+
key: 0,
|
|
2255
|
+
class: "absolute left-1/2 -translate-x-1/2 top-full mt-1 z-50 min-w-[560px] border border-base-200 rounded-xl bg-base-100 shadow-lg p-4"
|
|
2256
|
+
};
|
|
2257
|
+
const _hoisted_4$1 = { class: "flex flex-col gap-2" };
|
|
2258
|
+
const _hoisted_5$1 = { class: "flex items-center justify-between" };
|
|
2259
|
+
const _hoisted_6$1 = { class: "flex gap-2" };
|
|
2260
|
+
const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({
|
|
2261
|
+
__name: "TableFilter",
|
|
2262
|
+
props: {
|
|
2263
|
+
filters: {},
|
|
2264
|
+
filterSchema: {}
|
|
2265
|
+
},
|
|
2266
|
+
emits: ["changeFilters", "close"],
|
|
1771
2267
|
setup(__props, { emit: __emit }) {
|
|
1772
|
-
const
|
|
2268
|
+
const showFilters = vue.ref(false);
|
|
2269
|
+
const appliedCount = vue.ref(0);
|
|
2270
|
+
const containerRef = vue.ref(null);
|
|
2271
|
+
const onClickOutside = (event) => {
|
|
2272
|
+
const target = event.target;
|
|
2273
|
+
if (containerRef.value?.contains(target)) return;
|
|
2274
|
+
if (target.closest?.("[data-select-listbox]")) return;
|
|
2275
|
+
showFilters.value = false;
|
|
2276
|
+
};
|
|
2277
|
+
vue.onMounted(() => document.addEventListener("mousedown", onClickOutside));
|
|
2278
|
+
vue.onBeforeUnmount(
|
|
2279
|
+
() => document.removeEventListener("mousedown", onClickOutside)
|
|
2280
|
+
);
|
|
2281
|
+
const props = __props;
|
|
1773
2282
|
const emit = __emit;
|
|
1774
|
-
const
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
store = new FormStore(uri ?? "");
|
|
2283
|
+
const fields = vue.computed(() => {
|
|
2284
|
+
const properties = props.filterSchema?.properties;
|
|
2285
|
+
if (!properties) return [];
|
|
2286
|
+
return Object.entries(properties).map(([key, schema]) => ({
|
|
2287
|
+
value: key,
|
|
2288
|
+
label: schema.title ?? key
|
|
2289
|
+
}));
|
|
1782
2290
|
});
|
|
1783
|
-
const
|
|
1784
|
-
const
|
|
1785
|
-
const
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
2291
|
+
const defaultField = vue.computed(() => fields.value[0]?.value ?? "");
|
|
2292
|
+
const rows = vue.ref([]);
|
|
2293
|
+
const emptyRow = () => ({
|
|
2294
|
+
key: defaultField.value,
|
|
2295
|
+
value: "",
|
|
2296
|
+
operator: "contains"
|
|
2297
|
+
});
|
|
2298
|
+
vue.watch(
|
|
2299
|
+
() => props.filters,
|
|
2300
|
+
(newFilters) => {
|
|
2301
|
+
const parsed = jsonFormsCore.extractFilters(newFilters ?? []);
|
|
2302
|
+
rows.value = parsed.length > 0 ? parsed : [emptyRow()];
|
|
2303
|
+
appliedCount.value = parsed.length;
|
|
2304
|
+
},
|
|
2305
|
+
{ immediate: true }
|
|
2306
|
+
);
|
|
2307
|
+
const addRow = () => {
|
|
2308
|
+
rows.value = [
|
|
2309
|
+
...rows.value,
|
|
2310
|
+
{ key: defaultField.value, value: "", operator: "contains" }
|
|
2311
|
+
];
|
|
1796
2312
|
};
|
|
1797
|
-
const
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
return;
|
|
1801
|
-
}
|
|
1802
|
-
openModal();
|
|
1803
|
-
};
|
|
1804
|
-
const deleteFn = (data) => {
|
|
1805
|
-
ui.ModalService.showConfirm({
|
|
1806
|
-
title: "Delete record",
|
|
1807
|
-
message: "Are you sure to delete, the data will be lost?",
|
|
1808
|
-
onClose: (result) => {
|
|
1809
|
-
if (result.confirmed) {
|
|
1810
|
-
store.delete(data).then(() => {
|
|
1811
|
-
reload.value = Date.now();
|
|
1812
|
-
emit("delete", data);
|
|
1813
|
-
});
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
});
|
|
2313
|
+
const removeRow = (index) => {
|
|
2314
|
+
const updated = rows.value.filter((_, i) => i !== index);
|
|
2315
|
+
rows.value = updated.length > 0 ? updated : [emptyRow()];
|
|
1817
2316
|
};
|
|
1818
|
-
const
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
store.save(formData?.id, result.data).then(() => {
|
|
1830
|
-
reload.value = Date.now();
|
|
1831
|
-
emit("save", { id: formData?.id, data: result.data });
|
|
1832
|
-
});
|
|
1833
|
-
}
|
|
1834
|
-
},
|
|
1835
|
-
onEvents: (payload) => emit("events", payload)
|
|
1836
|
-
});
|
|
2317
|
+
const onApply = () => {
|
|
2318
|
+
const serialized = rows.value.filter((r) => r.key && r.value).map(jsonFormsCore.filterToString);
|
|
2319
|
+
appliedCount.value = serialized.length;
|
|
2320
|
+
emit("changeFilters", serialized);
|
|
2321
|
+
showFilters.value = false;
|
|
2322
|
+
};
|
|
2323
|
+
const onReset = () => {
|
|
2324
|
+
rows.value = [emptyRow()];
|
|
2325
|
+
appliedCount.value = 0;
|
|
2326
|
+
emit("changeFilters", []);
|
|
2327
|
+
showFilters.value = false;
|
|
1837
2328
|
};
|
|
1838
2329
|
return (_ctx, _cache) => {
|
|
1839
|
-
return vue.openBlock(), vue.createElementBlock(
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
2330
|
+
return vue.openBlock(), vue.createElementBlock("div", {
|
|
2331
|
+
ref_key: "containerRef",
|
|
2332
|
+
ref: containerRef,
|
|
2333
|
+
class: "relative inline-flex"
|
|
2334
|
+
}, [
|
|
2335
|
+
vue.createVNode(vue.unref(ui.Btn), {
|
|
2336
|
+
size: "xs",
|
|
2337
|
+
color: "ghost",
|
|
2338
|
+
class: "border-gray-300 text-base-content/50 h-8",
|
|
2339
|
+
onClick: _cache[0] || (_cache[0] = ($event) => showFilters.value = !showFilters.value)
|
|
2340
|
+
}, {
|
|
2341
|
+
default: vue.withCtx(() => [
|
|
2342
|
+
vue.createElementVNode("span", _hoisted_1$1, [
|
|
2343
|
+
vue.createVNode(vue.unref(ui.Icon), {
|
|
2344
|
+
icon: vue.unref(ui.IconEnum).Filter,
|
|
2345
|
+
size: "sm",
|
|
2346
|
+
class: "text-base-500"
|
|
2347
|
+
}, null, 8, ["icon"]),
|
|
2348
|
+
_cache[1] || (_cache[1] = vue.createTextVNode(" Filters ", -1)),
|
|
2349
|
+
appliedCount.value ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_2$1, vue.toDisplayString(appliedCount.value), 1)) : vue.createCommentVNode("", true)
|
|
2350
|
+
])
|
|
2351
|
+
]),
|
|
2352
|
+
_: 1
|
|
2353
|
+
}),
|
|
2354
|
+
showFilters.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$1, [
|
|
2355
|
+
vue.createElementVNode("div", _hoisted_4$1, [
|
|
2356
|
+
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(rows.value, (row, index) => {
|
|
2357
|
+
return vue.openBlock(), vue.createBlock(_sfc_main$2, {
|
|
2358
|
+
key: index,
|
|
2359
|
+
modelValue: rows.value[index],
|
|
2360
|
+
"onUpdate:modelValue": ($event) => rows.value[index] = $event,
|
|
2361
|
+
fields: fields.value,
|
|
2362
|
+
onRemove: ($event) => removeRow(index)
|
|
2363
|
+
}, null, 8, ["modelValue", "onUpdate:modelValue", "fields", "onRemove"]);
|
|
2364
|
+
}), 128))
|
|
2365
|
+
]),
|
|
2366
|
+
_cache[5] || (_cache[5] = vue.createElementVNode("div", { class: "divider my-3" }, null, -1)),
|
|
2367
|
+
vue.createElementVNode("div", _hoisted_5$1, [
|
|
1843
2368
|
vue.createVNode(vue.unref(ui.Btn), {
|
|
1844
2369
|
icon: vue.unref(ui.IconEnum).Plus,
|
|
1845
|
-
|
|
1846
|
-
onClick:
|
|
2370
|
+
color: "ghost",
|
|
2371
|
+
onClick: addRow
|
|
1847
2372
|
}, {
|
|
1848
|
-
default: vue.withCtx(() => [..._cache[
|
|
1849
|
-
vue.createTextVNode(" Add
|
|
2373
|
+
default: vue.withCtx(() => [..._cache[2] || (_cache[2] = [
|
|
2374
|
+
vue.createTextVNode(" Add filter ", -1)
|
|
1850
2375
|
])]),
|
|
1851
2376
|
_: 1
|
|
1852
|
-
}, 8, ["icon"])
|
|
2377
|
+
}, 8, ["icon"]),
|
|
2378
|
+
vue.createElementVNode("div", _hoisted_6$1, [
|
|
2379
|
+
vue.createVNode(vue.unref(ui.Btn), {
|
|
2380
|
+
color: "ghost",
|
|
2381
|
+
onClick: onReset
|
|
2382
|
+
}, {
|
|
2383
|
+
default: vue.withCtx(() => [..._cache[3] || (_cache[3] = [
|
|
2384
|
+
vue.createTextVNode(" Reset ", -1)
|
|
2385
|
+
])]),
|
|
2386
|
+
_: 1
|
|
2387
|
+
}),
|
|
2388
|
+
vue.createVNode(vue.unref(ui.Btn), { onClick: onApply }, {
|
|
2389
|
+
default: vue.withCtx(() => [..._cache[4] || (_cache[4] = [
|
|
2390
|
+
vue.createTextVNode(" Apply ", -1)
|
|
2391
|
+
])]),
|
|
2392
|
+
_: 1
|
|
2393
|
+
})
|
|
2394
|
+
])
|
|
1853
2395
|
])
|
|
2396
|
+
])) : vue.createCommentVNode("", true)
|
|
2397
|
+
], 512);
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
const _hoisted_1 = { class: "navbar bg-base-100" };
|
|
2402
|
+
const _hoisted_2 = { class: "navbar-start" };
|
|
2403
|
+
const _hoisted_3 = { key: 0 };
|
|
2404
|
+
const _hoisted_4 = { class: "navbar-center flex gap-2 items-center" };
|
|
2405
|
+
const _hoisted_5 = {
|
|
2406
|
+
key: 1,
|
|
2407
|
+
class: "flex gap-2"
|
|
2408
|
+
};
|
|
2409
|
+
const _hoisted_6 = { key: 0 };
|
|
2410
|
+
const _hoisted_7 = { class: "navbar-end" };
|
|
2411
|
+
const _hoisted_8 = { key: 0 };
|
|
2412
|
+
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
2413
|
+
__name: "TableToolbar",
|
|
2414
|
+
props: {
|
|
2415
|
+
filterSchema: {},
|
|
2416
|
+
filters: {},
|
|
2417
|
+
search: {},
|
|
2418
|
+
actions: {}
|
|
2419
|
+
},
|
|
2420
|
+
emits: ["updateSearch", "updateFilters", "action"],
|
|
2421
|
+
setup(__props, { emit: __emit }) {
|
|
2422
|
+
const props = __props;
|
|
2423
|
+
const emit = __emit;
|
|
2424
|
+
const searchQuery = vue.ref(props.search ?? "");
|
|
2425
|
+
let searchTimeout = null;
|
|
2426
|
+
const onSearchInput = (value) => {
|
|
2427
|
+
searchQuery.value = value;
|
|
2428
|
+
if (searchTimeout) clearTimeout(searchTimeout);
|
|
2429
|
+
searchTimeout = setTimeout(() => emit("updateSearch", value), 300);
|
|
2430
|
+
};
|
|
2431
|
+
const onChangeFilters = (filters) => {
|
|
2432
|
+
emit("updateFilters", filters);
|
|
2433
|
+
};
|
|
2434
|
+
return (_ctx, _cache) => {
|
|
2435
|
+
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
|
|
2436
|
+
vue.createElementVNode("div", _hoisted_2, [
|
|
2437
|
+
_ctx.$slots.left ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3, [
|
|
2438
|
+
vue.renderSlot(_ctx.$slots, "left")
|
|
2439
|
+
])) : vue.createCommentVNode("", true)
|
|
1854
2440
|
]),
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
2441
|
+
vue.createElementVNode("div", _hoisted_4, [
|
|
2442
|
+
vue.createVNode(vue.unref(ui.Input), {
|
|
2443
|
+
placeholder: "Search...",
|
|
2444
|
+
value: searchQuery.value,
|
|
2445
|
+
size: "sm",
|
|
2446
|
+
width: "w-48",
|
|
2447
|
+
clearable: true,
|
|
2448
|
+
onInput: _cache[0] || (_cache[0] = ($event) => onSearchInput($event.target.value))
|
|
2449
|
+
}, null, 8, ["value"]),
|
|
2450
|
+
__props.filterSchema ? (vue.openBlock(), vue.createBlock(_sfc_main$1, {
|
|
2451
|
+
key: 0,
|
|
2452
|
+
filters: __props.filters ?? [],
|
|
2453
|
+
"filter-schema": __props.filterSchema,
|
|
2454
|
+
onChangeFilters
|
|
2455
|
+
}, null, 8, ["filters", "filter-schema"])) : vue.createCommentVNode("", true),
|
|
2456
|
+
__props.actions ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5, [
|
|
2457
|
+
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.actions, (action) => {
|
|
2458
|
+
return vue.openBlock(), vue.createBlock(vue.unref(ui.Btn), {
|
|
2459
|
+
key: action.label ?? action.tooltip,
|
|
2460
|
+
size: "xs",
|
|
2461
|
+
color: "ghost",
|
|
2462
|
+
class: "border-gray-300 text-base-content/50 h-8",
|
|
2463
|
+
icon: action.icon,
|
|
2464
|
+
tooltip: action.tooltip,
|
|
2465
|
+
onClick: action.action
|
|
2466
|
+
}, {
|
|
2467
|
+
default: vue.withCtx(() => [
|
|
2468
|
+
action.label ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_6, vue.toDisplayString(action.label), 1)) : vue.createCommentVNode("", true)
|
|
2469
|
+
]),
|
|
2470
|
+
_: 2
|
|
2471
|
+
}, 1032, ["icon", "tooltip", "onClick"]);
|
|
2472
|
+
}), 128))
|
|
2473
|
+
])) : vue.createCommentVNode("", true)
|
|
2474
|
+
]),
|
|
2475
|
+
vue.createElementVNode("div", _hoisted_7, [
|
|
2476
|
+
_ctx.$slots.right ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_8, [
|
|
2477
|
+
vue.renderSlot(_ctx.$slots, "right")
|
|
2478
|
+
])) : vue.createCommentVNode("", true)
|
|
2479
|
+
])
|
|
2480
|
+
]);
|
|
1874
2481
|
};
|
|
1875
2482
|
}
|
|
1876
2483
|
});
|
|
2484
|
+
exports.FilterRowInput = _sfc_main$2;
|
|
1877
2485
|
exports.FormModalEmits = FormModalEmits;
|
|
1878
2486
|
exports.FormModalProperties = FormModalProperties;
|
|
1879
|
-
exports.JsonForm = _sfc_main$
|
|
1880
|
-
exports.JsonFormModal = _sfc_main$
|
|
2487
|
+
exports.JsonForm = _sfc_main$4;
|
|
2488
|
+
exports.JsonFormModal = _sfc_main$j;
|
|
1881
2489
|
exports.JsonFormModalService = JsonFormModalService;
|
|
1882
|
-
exports.
|
|
1883
|
-
exports.
|
|
1884
|
-
exports.TableComponent = _sfc_main$g;
|
|
2490
|
+
exports.ReadonlyLabel = _sfc_main$u;
|
|
2491
|
+
exports.TableComponent = _sfc_main$3;
|
|
1885
2492
|
exports.TableComponentEmits = TableComponentEmits;
|
|
1886
2493
|
exports.TableComponentProperties = TableComponentProperties;
|
|
2494
|
+
exports.TableFilter = _sfc_main$1;
|
|
2495
|
+
exports.TableToolbar = _sfc_main;
|
|
2496
|
+
exports.ViewDetailValue = _sfc_main$l;
|
|
2497
|
+
exports.cellFormatIs = cellFormatIs;
|
|
2498
|
+
exports.cellTypeIs = cellTypeIs;
|
|
1887
2499
|
exports.createRepository = createRepository;
|
|
2500
|
+
exports.customRenderers = customRenderers;
|
|
2501
|
+
exports.defaultCellRenderers = defaultCellRenderers;
|
|
2502
|
+
exports.findCellRenderer = findCellRenderer;
|
|
1888
2503
|
exports.formatError = formatError;
|
|
2504
|
+
exports.getNestedValue = getNestedValue;
|
|
2505
|
+
exports.isLink = isLink;
|
|
2506
|
+
exports.optionIsIgnoreCase = optionIsIgnoreCase;
|
|
1889
2507
|
exports.provideFormEvents = provideFormEvents;
|
|
2508
|
+
exports.provideHttpClient = provideHttpClient;
|
|
2509
|
+
exports.readonlyRenderers = readonlyRenderers;
|
|
1890
2510
|
exports.registerZodErrorMap = registerZodErrorMap;
|
|
2511
|
+
exports.useControlBinding = useControlBinding;
|
|
2512
|
+
exports.useCustomControlBinding = useCustomControlBinding;
|
|
2513
|
+
exports.useCustomReadonlyControlBinding = useCustomReadonlyControlBinding;
|
|
2514
|
+
exports.useDisplayValue = useDisplayValue;
|
|
1891
2515
|
exports.useFormEvents = useFormEvents;
|
|
1892
|
-
exports.
|
|
2516
|
+
exports.useHttpClient = useHttpClient;
|
|
2517
|
+
exports.useReadonlyControlBinding = useReadonlyControlBinding;
|