@ghentcdh/json-forms-vue 2.0.0 → 3.0.1

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.
Files changed (75) hide show
  1. package/index.d.ts +6 -19
  2. package/index.js +8 -2516
  3. package/package.json +8 -14
  4. package/composables/useFormEvents.d.ts +0 -34
  5. package/composables/useHttpClient.d.ts +0 -12
  6. package/forms/Dispatch.vue.d.ts +0 -10
  7. package/forms/FormComponent.properties.d.ts +0 -64
  8. package/forms/FormComponent.vue.d.ts +0 -95
  9. package/forms/errorMessages.d.ts +0 -10
  10. package/forms/errorMode.d.ts +0 -5
  11. package/forms/modal/FormModal.properties.d.ts +0 -75
  12. package/forms/modal/FormModal.vue.d.ts +0 -353
  13. package/forms/modal/FormModalService.d.ts +0 -42
  14. package/forms/renderers/array/ArrayRenderer.vue.d.ts +0 -7
  15. package/forms/renderers/array/ArrayRenderers.d.ts +0 -10
  16. package/forms/renderers/controls/AutocompleteControlRenderer.vue.d.ts +0 -7
  17. package/forms/renderers/controls/BooleanControlRenderer.vue.d.ts +0 -7
  18. package/forms/renderers/controls/MarkdownControlRenderer.vue.d.ts +0 -7
  19. package/forms/renderers/controls/MultiSelectControlRenderer.vue.d.ts +0 -7
  20. package/forms/renderers/controls/NumberControlRenderer.vue.d.ts +0 -7
  21. package/forms/renderers/controls/SelectControlRenderer.vue.d.ts +0 -7
  22. package/forms/renderers/controls/StringControlRenderer.vue.d.ts +0 -7
  23. package/forms/renderers/controls/TextAreaControlRenderer.vue.d.ts +0 -7
  24. package/forms/renderers/controls/composables/useControlBinding.d.ts +0 -36
  25. package/forms/renderers/controls/composables/useFetchOption.d.ts +0 -17
  26. package/forms/renderers/controls/composables/useInput.d.ts +0 -24
  27. package/forms/renderers/controls/composables/useReadonlyBinding.d.ts +0 -135
  28. package/forms/renderers/controls/composables/useSelectBinding.d.ts +0 -29
  29. package/forms/renderers/controls/index.d.ts +0 -10
  30. package/forms/renderers/controls/readonly/ControlReadonlyRenderer.vue.d.ts +0 -7
  31. package/forms/renderers/controls/readonly/ReadonlyLabel.vue.d.ts +0 -254
  32. package/forms/renderers/controls/readonly/__tests__/formatDate.spec.d.ts +0 -1
  33. package/forms/renderers/controls/readonly/constants.d.ts +0 -2
  34. package/forms/renderers/controls/readonly/displayValue/BooleanValue.vue.d.ts +0 -46
  35. package/forms/renderers/controls/readonly/displayValue/DateValue.vue.d.ts +0 -46
  36. package/forms/renderers/controls/readonly/displayValue/LinkValue.vue.d.ts +0 -46
  37. package/forms/renderers/controls/readonly/displayValue/MarkdownValue.vue.d.ts +0 -46
  38. package/forms/renderers/controls/readonly/displayValue/NotSetValue.vue.d.ts +0 -46
  39. package/forms/renderers/controls/readonly/displayValue/NumberValue.vue.d.ts +0 -46
  40. package/forms/renderers/controls/readonly/displayValue/ObjectValue.vue.d.ts +0 -46
  41. package/forms/renderers/controls/readonly/displayValue/StringValue.vue.d.ts +0 -46
  42. package/forms/renderers/controls/readonly/displayValue/ViewDetailValue.vue.d.ts +0 -46
  43. package/forms/renderers/controls/readonly/displayValue/displayValue.properties.d.ts +0 -23
  44. package/forms/renderers/controls/readonly/displayValue/formatDate.d.ts +0 -25
  45. package/forms/renderers/controls/readonly/displayValue/index.d.ts +0 -1
  46. package/forms/renderers/controls/readonly/index.d.ts +0 -10
  47. package/forms/renderers/controls/readonly/useDisplayValue.d.ts +0 -1
  48. package/forms/renderers/controls/resource.d.ts +0 -262
  49. package/forms/renderers/index.d.ts +0 -43
  50. package/forms/renderers/layout/CollapseLayoutRenderer.vue.d.ts +0 -7
  51. package/forms/renderers/layout/LayoutRenderer.vue.d.ts +0 -7
  52. package/forms/renderers/layout/LayoutRenderers.d.ts +0 -20
  53. package/forms/renderers/layout/ReadOnlyLayoutRenderer.vue.d.ts +0 -7
  54. package/forms/renderers/layout/colspan.d.ts +0 -1
  55. package/forms/renderers/registry.d.ts +0 -7
  56. package/forms/scope.d.ts +0 -4
  57. package/forms/types.d.ts +0 -36
  58. package/http-client.d.ts +0 -14
  59. package/repository/CrudRepository.d.ts +0 -34
  60. package/repository/index.d.ts +0 -1
  61. package/table/TableComponent.properties.d.ts +0 -49
  62. package/table/TableComponent.vue.d.ts +0 -92
  63. package/table/TableToolbar.vue.d.ts +0 -36
  64. package/table/cells/index.d.ts +0 -10
  65. package/table/filter/FilterRowInput.vue.d.ts +0 -17
  66. package/table/filter/TableFilter.vue.d.ts +0 -16
  67. package/table/index.d.ts +0 -6
  68. package/testers/__tests__/jsonforms-testers.spec.d.ts +0 -1
  69. package/testers/__tests__/tester.spec.d.ts +0 -1
  70. package/testers/jsonforms-testers.d.ts +0 -27
  71. package/testers/tester.d.ts +0 -16
  72. package/testing/index.d.ts +0 -0
  73. package/testing.js +0 -1
  74. package/view/modal/ViewModal.properties.d.ts +0 -82
  75. package/view/modal/ViewModal.vue.d.ts +0 -357
package/index.js CHANGED
@@ -1,2517 +1,9 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const vue = require("vue");
4
- const ui = require("@ghentcdh/ui");
5
- const veeValidate = require("vee-validate");
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) {
14
- return (_ctx, _cache) => {
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)
18
- ]),
19
- vue.createElementVNode("div", _hoisted_2$8, [
20
- vue.renderSlot(_ctx.$slots, "default")
21
- ])
22
- ], 64);
23
- };
24
- }
1
+ var _ghentcdh_crouton_forms_vue = require("@ghentcdh/crouton-forms-vue");
2
+ Object.keys(_ghentcdh_crouton_forms_vue).forEach(function(k) {
3
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
4
+ enumerable: true,
5
+ get: function() {
6
+ return _ghentcdh_crouton_forms_vue[k];
7
+ }
8
+ });
25
9
  });
26
- const errorPatterns = [
27
- [/^Invalid input: expected \w+, received undefined$/, "This field is required"],
28
- [/^Invalid input: expected \w+, received null$/, "This field is required"],
29
- [/^Expected string, received/, "Invalid value"],
30
- [/^String must contain at least (\d+)/, "Must be at least $1 characters"],
31
- [/^Number must be greater than or equal to (\d+)/, "Minimum value is $1"],
32
- [/^Number must be less than or equal to (\d+)/, "Maximum value is $1"]
33
- ];
34
- const formatError = (message) => {
35
- if (!message) return void 0;
36
- for (const [pattern, replacement] of errorPatterns) {
37
- if (pattern.test(message)) {
38
- return message.replace(pattern, replacement);
39
- }
40
- }
41
- return message;
42
- };
43
- const registerZodErrorMap = () => {
44
- zod.z.config({
45
- customError: (issue) => {
46
- if (issue.code === "invalid_type" && issue.received === "undefined") {
47
- return { message: "This field is required" };
48
- }
49
- if (issue.code === "invalid_type" && issue.received === "null") {
50
- return { message: "This field is required" };
51
- }
52
- if (issue.code === "too_small") {
53
- const i = issue;
54
- if (i.type === "string" && i.minimum === 1) {
55
- return { message: "This field is required" };
56
- }
57
- if (i.type === "string") {
58
- return { message: `Must be at least ${i.minimum} characters` };
59
- }
60
- }
61
- if (issue.code === "too_big") {
62
- const i = issue;
63
- if (i.type === "string") {
64
- return { message: `Must be at most ${i.maximum} characters` };
65
- }
66
- }
67
- return { message: issue.message ?? "" };
68
- }
69
- });
70
- };
71
- const ERROR_MODE_KEY = /* @__PURE__ */ Symbol("errorMode");
72
- const FORM_SUBMITTED_KEY = /* @__PURE__ */ Symbol("formSubmitted");
73
- const FORM_READONLY_KEY = /* @__PURE__ */ Symbol("formReadonly");
74
- const scopeToPath = (scope) => {
75
- if (!scope) return "";
76
- return scope.replace(/^#\//, "").split("/").filter((s) => s !== "properties").join(".");
77
- };
78
- const resolveSchema = (root, scope) => {
79
- const segments = scope.replace(/^#\//, "").split("/").filter(Boolean);
80
- return segments.reduce((acc, key) => acc?.[key], root);
81
- };
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 = {
301
- key: 1,
302
- class: "inline-flex items-center gap-1 text-base-content/50"
303
- };
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").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,
486
- setup(__props) {
487
- const props = __props;
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
- };
497
- return (_ctx, _cache) => {
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
- });
515
- };
516
- }
517
- });
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;
543
- }
544
- if (value !== null && typeof value === "object" && opts.key) {
545
- return value[opts.key] ?? null;
546
- }
547
- return value ?? null;
548
- };
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 = {
645
- key: 0,
646
- class: "text-sm text-base-content/50"
647
- };
648
- const _hoisted_4$2 = { key: 1 };
649
- const _sfc_main$k = /* @__PURE__ */ vue.defineComponent({
650
- __name: "ArrayRenderer",
651
- props: {
652
- uischema: {},
653
- schema: {}
654
- },
655
- setup(__props) {
656
- const props = __props;
657
- const parentPrefix = vue.inject("pathPrefix", "");
658
- const scope = props.uischema.scope;
659
- const scopePath = scopeToPath(scope);
660
- const path = parentPrefix ? `${parentPrefix}.${scopePath}` : scopePath;
661
- const rootSchema = vue.inject("rootSchema");
662
- const arraySchema = resolveSchema(rootSchema, scope);
663
- const itemSchema = arraySchema?.items ?? {};
664
- const { fields, push, remove } = veeValidate.useFieldArray(path);
665
- if (fields.value.length === 0) {
666
- push({});
667
- }
668
- const opts = props.uischema.options ?? {};
669
- const layout = opts.layout ?? "column";
670
- const showActions = vue.computed(() => !opts.hideActions);
671
- const detail = vue.computed(
672
- () => props.uischema._detail ?? opts.detail
673
- );
674
- const childElements = vue.computed(() => {
675
- if (!detail.value) {
676
- return props.uischema.elements ?? [];
677
- }
678
- const type = detail.value.type;
679
- if (type && type !== "Control") {
680
- return [detail.value];
681
- }
682
- return detail.value.elements ?? [detail.value];
683
- });
684
- return (_ctx, _cache) => {
685
- return vue.openBlock(), vue.createElementBlock("div", null, [
686
- vue.createElementVNode("div", {
687
- class: vue.normalizeClass([
688
- "flex gap-2",
689
- vue.unref(layout) === "row" ? "flex-row items-center" : "flex-col"
690
- ])
691
- }, [
692
- (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(fields), (entry, index) => {
693
- return vue.openBlock(), vue.createElementBlock("div", {
694
- key: entry.key,
695
- class: "flex-1"
696
- }, [
697
- vue.createElementVNode("div", {
698
- class: vue.normalizeClass([
699
- "flex gap-2",
700
- vue.unref(layout) === "row" ? "flex-col" : "flex-row items-center"
701
- ])
702
- }, [
703
- vue.createElementVNode("div", _hoisted_1$a, [
704
- (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(childElements.value, (child, ci) => {
705
- return vue.openBlock(), vue.createBlock(_sfc_main$5, {
706
- key: ci,
707
- uischema: child,
708
- schema: vue.unref(itemSchema),
709
- "path-prefix": `${vue.unref(path)}[${index}]`
710
- }, null, 8, ["uischema", "schema", "path-prefix"]);
711
- }), 128))
712
- ]),
713
- showActions.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$4, [
714
- vue.unref(fields).length > 1 ? (vue.openBlock(), vue.createBlock(vue.unref(ui.Btn), {
715
- key: 0,
716
- icon: vue.unref(ui.IconEnum).Delete,
717
- outline: true,
718
- onClick: ($event) => vue.unref(remove)(index)
719
- }, null, 8, ["icon", "onClick"])) : vue.createCommentVNode("", true)
720
- ])) : vue.createCommentVNode("", true)
721
- ], 2)
722
- ]);
723
- }), 128)),
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, [
726
- vue.createVNode(vue.unref(ui.Btn), {
727
- icon: vue.unref(ui.IconEnum).Plus,
728
- outline: true,
729
- onClick: _cache[0] || (_cache[0] = ($event) => vue.unref(push)({}))
730
- }, {
731
- default: vue.withCtx(() => [..._cache[1] || (_cache[1] = [
732
- vue.createTextVNode(" Add ", -1)
733
- ])]),
734
- _: 1
735
- }, 8, ["icon"])
736
- ])) : vue.createCommentVNode("", true)
737
- ], 2)
738
- ]);
739
- };
740
- }
741
- });
742
- const arrayRenderers = [
743
- { tester: rankWith(12, isArrayRenderer), renderer: _sfc_main$k }
744
- ];
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
- ]);
757
- const OperationSchema = zod.z.object({
758
- uri: zod.z.string(),
759
- method: MethodSchema
760
- });
761
- const OperationsSchema = zod.z.string().or(OperationSchema);
762
- const BooleanOperationSchema = zod.z.boolean().or(OperationSchema).optional().default(false);
763
- const Operations = zod.z.object({
764
- findAll: BooleanOperationSchema,
765
- findOne: BooleanOperationSchema,
766
- create: BooleanOperationSchema,
767
- update: BooleanOperationSchema,
768
- delete: BooleanOperationSchema,
769
- lookup: OperationsSchema.optional()
770
- });
771
- const OperationMap = {
772
- create: "post",
773
- delete: "delete",
774
- findAll: "get",
775
- findOne: "get",
776
- lookup: "get",
777
- update: "patch"
778
- };
779
- const schemaDef = zod.z.object({
780
- ui: zod.z.any().optional(),
781
- data: zod.z.any()
782
- });
783
- const ResourceSchema = zod.z.object({
784
- id: zod.z.string(),
785
- uri: zod.z.string(),
786
- operations: Operations,
787
- schema: schemaDef.optional(),
788
- schemas: zod.z.object({
789
- form: schemaDef.optional()
790
- }).optional()
791
- }).transform((data) => {
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);
795
- }
796
- const operations = {};
797
- for (const k in OperationMap) {
798
- const key = k;
799
- const defaultOperation = OperationMap[key];
800
- const operation = data.operations[key];
801
- const mapResourceSchema = () => {
802
- if (operation === void 0 || operation === false) return null;
803
- if (operation === true)
804
- return { uri: data.uri, method: defaultOperation };
805
- if (typeof operation === "string")
806
- return { uri: operation, method: "get" };
807
- return {
808
- uri: operation.uri,
809
- method: operation.method?.toLowerCase() ?? defaultOperation
810
- };
811
- };
812
- operations[key] = mapResourceSchema();
813
- }
814
- return {
815
- ...data,
816
- schema,
817
- operations
818
- };
819
- });
820
- const getResourceSchema = async (resourceUri, http) => {
821
- return http.get(resourceUri).then((response) => {
822
- const resource = ResourceSchema.safeParse(response.data);
823
- if (!resource.success)
824
- throw new Error(`Invalid resource schema: ${resource.error}`);
825
- return resource.data;
826
- });
827
- };
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) => {
840
- return {
841
- fetchOptions: (searchTerm, signal) => {
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
- });
848
- }
849
- };
850
- };
851
- const useResourceOptions = async (options, http, formValues) => {
852
- const resource = await getResourceSchema(options.resource, http);
853
- const lookup = resource.operations.lookup;
854
- return {
855
- fetchOptions: (searchTerm, signal) => {
856
- const uri = resolvePlaceholders(lookup.uri, formValues, searchTerm);
857
- const method = lookup.method;
858
- return http[method](uri, { signal }).then((data) => data.data[options.dataField ?? "data"]);
859
- },
860
- enableCreate: !!resource.operations.create && resource.schema.ui,
861
- form: resource.operations.create ? {
862
- ui_schema: resource.schema.ui,
863
- json_schema: resource.schema.data,
864
- title: `Create new ${resource.id}`,
865
- create: async (data) => {
866
- const create = resource.operations.create;
867
- return http[create.method](create.uri, data).then((result) => result.data);
868
- }
869
- } : null
870
- };
871
- };
872
- const useFetchOptions = async (options, http, formValues = {}) => {
873
- let config = {};
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
- );
886
- return {
887
- fetchOptions: null,
888
- labelKey: options.labelKey,
889
- valueKey: options.valueKey,
890
- enableCreate: options.enableCreate ?? false,
891
- form: null,
892
- ...config
893
- };
894
- };
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;
902
- };
903
- const useSelectInput = (...fields) => (uischema, schema, field) => {
904
- const opts = uischema.options ?? {};
905
- return vue.computed(() => {
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;
916
- });
917
- };
918
- const useSelectBinding = useCustomControlBinding({
919
- useProps: useSelectInput(
920
- "options",
921
- "values",
922
- "uri",
923
- "resource",
924
- "dataField",
925
- "labelKey",
926
- "valueKey",
927
- "clearable",
928
- "storeValue"
929
- )
930
- });
931
- const useAutocompleteBinding = useCustomControlBinding({
932
- useProps: useSelectInput(
933
- "options",
934
- "labelKey",
935
- "valueKey",
936
- "uri",
937
- "freeText",
938
- "enableCreate",
939
- "dataField",
940
- "skipAuth"
941
- )
942
- });
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({
995
- __name: "FormModal",
996
- props: /* @__PURE__ */ vue.mergeModels(FormModalProperties, {
997
- "modelValue": {},
998
- "modelModifiers": {}
999
- }),
1000
- emits: /* @__PURE__ */ vue.mergeModels(FormModalEmits, ["update:modelValue"]),
1001
- setup(__props, { emit: __emit }) {
1002
- const properties = __props;
1003
- const id = `edit_${Math.floor(Math.random() * 1e3)}`;
1004
- const formRef = vue.ref();
1005
- const valid = vue.ref(false);
1006
- const formData = vue.useModel(__props, "modelValue");
1007
- const emits = __emit;
1008
- if (properties.data) {
1009
- formData.value = properties.data;
1010
- }
1011
- const onCancel = () => {
1012
- formData.value = {};
1013
- emits("closeModal", null);
1014
- };
1015
- const onChange = (data) => {
1016
- formData.value = data;
1017
- };
1018
- const onSubmit = () => {
1019
- formRef.value?.markSubmitted();
1020
- if (!valid.value) return;
1021
- emits("closeModal", { data: formData.value, valid: valid.value });
1022
- };
1023
- const onErrors = (errors) => {
1024
- emits("errors", errors);
1025
- valid.value = !errors || (Array.isArray(errors) ? errors.length === 0 : Object.keys(errors).length === 0);
1026
- };
1027
- vue.watch(valid, (newValid, oldValid) => {
1028
- if (newValid !== oldValid) {
1029
- emits("valid", newValid);
1030
- }
1031
- });
1032
- return (_ctx, _cache) => {
1033
- return vue.openBlock(), vue.createBlock(vue.unref(ui.Modal), vue.mergeProps(properties, {
1034
- open: true,
1035
- "disable-close": false,
1036
- width: _ctx.modalSize,
1037
- onCloseModal: onCancel
1038
- }), {
1039
- content: vue.withCtx(() => [
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
- ])
1058
- ]),
1059
- actions: vue.withCtx(() => [
1060
- vue.createVNode(vue.unref(ui.Btn), {
1061
- color: vue.unref(ui.Color).secondary,
1062
- outline: true,
1063
- "aria-label": _ctx.cancelLabel,
1064
- onClick: onCancel
1065
- }, {
1066
- default: vue.withCtx(() => [
1067
- vue.createTextVNode(vue.toDisplayString(_ctx.cancelLabel), 1)
1068
- ]),
1069
- _: 1
1070
- }, 8, ["color", "aria-label"]),
1071
- vue.createVNode(vue.unref(ui.Btn), {
1072
- disabled: !valid.value,
1073
- "aria-label": _ctx.saveLabel,
1074
- onClick: onSubmit
1075
- }, {
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
- ]),
1234
- _: 1
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)
1249
- ]),
1250
- _: 3
1251
- }, 16, ["width"]);
1252
- };
1253
- }
1254
- });
1255
- class JsonFormModalService {
1256
- static openModal({
1257
- initialData,
1258
- modalTitle,
1259
- schema,
1260
- uiSchema,
1261
- modalSize,
1262
- onClose,
1263
- onEvents,
1264
- http,
1265
- renderers
1266
- }) {
1267
- ui.ModalService.openModal({
1268
- component: _sfc_main$j,
1269
- props: {
1270
- schema,
1271
- uiSchema,
1272
- modalSize,
1273
- data: initialData ?? {},
1274
- modalTitle,
1275
- onClose,
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
1314
- }
1315
- });
1316
- }
1317
- }
1318
- const _sfc_main$h = /* @__PURE__ */ vue.defineComponent({
1319
- __name: "AutocompleteControlRenderer",
1320
- props: {
1321
- uischema: {},
1322
- schema: {}
1323
- },
1324
- setup(__props) {
1325
- const props = __props;
1326
- const {
1327
- wrapper,
1328
- value,
1329
- field,
1330
- onBlur,
1331
- onChange: onFieldChange,
1332
- appliedOptions
1333
- } = useAutocompleteBinding(props.uischema, props.schema);
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
- );
1344
- const onChange = (val) => {
1345
- setValue(val);
1346
- onFieldChange();
1347
- };
1348
- const formEvents = useFormEvents();
1349
- const path = scopeToPath(props.uischema.scope);
1350
- const setValue = (result) => {
1351
- if (!result || !fetchOptions.value) {
1352
- field.setValue(result);
1353
- return;
1354
- }
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
- }
1361
- const keys = [valueKey, labelKey].filter(Boolean);
1362
- if (keys.length === 0) {
1363
- field.setValue(result);
1364
- return;
1365
- }
1366
- const stripped = Object.fromEntries(keys.filter((k) => k in result).map((k) => [k, result[k]]));
1367
- field.setValue(stripped);
1368
- };
1369
- const onCreate = () => {
1370
- if (fetchOptions.value?.enableCreate === false) return;
1371
- const form = fetchOptions.value.form;
1372
- if (form) {
1373
- JsonFormModalService.openModal({
1374
- schema: form.json_schema,
1375
- uiSchema: form.ui_schema,
1376
- modalTitle: `Create new ${wrapper.value.label}`,
1377
- http,
1378
- onClose: (result) => {
1379
- if (!result || !result.valid) return;
1380
- form.create(result.data).then((res) => {
1381
- setValue(res);
1382
- });
1383
- }
1384
- });
1385
- return;
1386
- }
1387
- formEvents.dispatch({
1388
- event: "create",
1389
- type: path,
1390
- data: value.value,
1391
- onSuccess: setValue
1392
- });
1393
- };
1394
- return (_ctx, _cache) => {
1395
- return fetchOptions.value ? (vue.openBlock(), vue.createBlock(vue.unref(ui.Autocomplete), vue.mergeProps({ key: 0 }, vue.unref(wrapper), {
1396
- "model-value": vue.unref(value),
1397
- "fetch-options": fetchOptions.value.fetchOptions,
1398
- "label-key": fetchOptions.value.labelKey,
1399
- "value-key": fetchOptions.value.valueKey,
1400
- "enable-create": fetchOptions.value.enableCreate,
1401
- onChange,
1402
- onBlur: vue.unref(onBlur),
1403
- onCreate
1404
- }), null, 16, ["model-value", "fetch-options", "label-key", "value-key", "enable-create", "onBlur"])) : vue.createCommentVNode("", true);
1405
- };
1406
- }
1407
- });
1408
- const _sfc_main$g = /* @__PURE__ */ vue.defineComponent({
1409
- __name: "BooleanControlRenderer",
1410
- props: {
1411
- uischema: {},
1412
- schema: {}
1413
- },
1414
- setup(__props) {
1415
- const props = __props;
1416
- const useBooleanBinding = useCustomControlBinding({
1417
- setDefaultValue: (field2) => {
1418
- if (field2.value.value === void 0) field2.setValue(false);
1419
- }
1420
- });
1421
- const { wrapper, value, field, onBlur, onChange: onFieldChange } = useBooleanBinding(props.uischema, props.schema);
1422
- const onChange = (val) => {
1423
- field.setValue(Boolean(val) ?? false);
1424
- onFieldChange();
1425
- };
1426
- return (_ctx, _cache) => {
1427
- return vue.openBlock(), vue.createBlock(vue.unref(ui.Checkbox), vue.mergeProps(vue.unref(wrapper), {
1428
- "model-value": vue.unref(value),
1429
- onChange,
1430
- onBlur: vue.unref(onBlur)
1431
- }), null, 16, ["model-value", "onBlur"]);
1432
- };
1433
- }
1434
- });
1435
- const _sfc_main$f = /* @__PURE__ */ vue.defineComponent({
1436
- __name: "MarkdownControlRenderer",
1437
- props: {
1438
- uischema: {},
1439
- schema: {}
1440
- },
1441
- setup(__props) {
1442
- const props = __props;
1443
- const {
1444
- wrapper,
1445
- value,
1446
- field,
1447
- onBlur,
1448
- onChange: onFieldChange
1449
- } = useControlBinding(props.uischema, props.schema);
1450
- const onChange = (val) => {
1451
- field.setValue(val);
1452
- onFieldChange();
1453
- };
1454
- return (_ctx, _cache) => {
1455
- return vue.openBlock(), vue.createBlock(vue.unref(ui.Markdown), vue.mergeProps(vue.unref(wrapper), {
1456
- "model-value": vue.unref(value),
1457
- onChange,
1458
- onBlur: vue.unref(onBlur)
1459
- }), null, 16, ["model-value", "onBlur"]);
1460
- };
1461
- }
1462
- });
1463
- const _sfc_main$e = /* @__PURE__ */ vue.defineComponent({
1464
- __name: "MultiSelectControlRenderer",
1465
- props: {
1466
- uischema: {},
1467
- schema: {}
1468
- },
1469
- setup(__props) {
1470
- const props = __props;
1471
- const {
1472
- wrapper,
1473
- value,
1474
- field,
1475
- onBlur,
1476
- onChange: onFieldChange,
1477
- appliedOptions
1478
- } = useSelectBinding(props.uischema, props.schema);
1479
- const onChange = (val) => {
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);
1484
- onFieldChange();
1485
- };
1486
- return (_ctx, _cache) => {
1487
- return vue.openBlock(), vue.createBlock(vue.unref(ui.MultiSelect), vue.mergeProps(vue.unref(wrapper), {
1488
- "model-value": vue.unref(value),
1489
- onChange,
1490
- onBlur: vue.unref(onBlur)
1491
- }), null, 16, ["model-value", "onBlur"]);
1492
- };
1493
- }
1494
- });
1495
- const _sfc_main$d = /* @__PURE__ */ vue.defineComponent({
1496
- __name: "NumberControlRenderer",
1497
- props: {
1498
- uischema: {},
1499
- schema: {}
1500
- },
1501
- setup(__props) {
1502
- const props = __props;
1503
- const { wrapper, value, onBlur, onChange } = useControlBinding(
1504
- props.uischema,
1505
- props.schema,
1506
- { defaultType: "number" }
1507
- );
1508
- return (_ctx, _cache) => {
1509
- return vue.openBlock(), vue.createBlock(vue.unref(ui.InputNumber), vue.mergeProps(vue.unref(wrapper), {
1510
- modelValue: vue.unref(value),
1511
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.isRef(value) ? value.value = $event : null),
1512
- steps: 0.01,
1513
- onBlur: vue.unref(onBlur),
1514
- onChange: vue.unref(onChange)
1515
- }), null, 16, ["modelValue", "onBlur", "onChange"]);
1516
- };
1517
- }
1518
- });
1519
- const _sfc_main$c = /* @__PURE__ */ vue.defineComponent({
1520
- __name: "SelectControlRenderer",
1521
- props: {
1522
- uischema: {},
1523
- schema: {}
1524
- },
1525
- setup(__props) {
1526
- const props = __props;
1527
- const {
1528
- wrapper,
1529
- value,
1530
- field,
1531
- onBlur,
1532
- onChange: onFieldChange,
1533
- appliedOptions
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
- );
1566
- const selectOptions = vue.computed(() => {
1567
- if (isRemote.value) return remoteOptions.value;
1568
- const opts = appliedOptions.value;
1569
- return opts.options ?? opts.values ?? [];
1570
- });
1571
- const onChange = (val) => {
1572
- const opts = appliedOptions.value;
1573
- const stored = opts.storeValue && val && typeof val === "object" ? val[valueKey.value] : val;
1574
- field.setValue(stored);
1575
- onFieldChange();
1576
- };
1577
- return (_ctx, _cache) => {
1578
- return vue.openBlock(), vue.createBlock(vue.unref(ui.SelectComponent), vue.mergeProps(vue.unref(wrapper), {
1579
- "model-value": vue.unref(value),
1580
- options: selectOptions.value,
1581
- clearable: vue.unref(appliedOptions).clearable ?? true,
1582
- onChange,
1583
- onBlur: vue.unref(onBlur)
1584
- }), null, 16, ["model-value", "options", "clearable", "onBlur"]);
1585
- };
1586
- }
1587
- });
1588
- const _sfc_main$b = /* @__PURE__ */ vue.defineComponent({
1589
- __name: "StringControlRenderer",
1590
- props: {
1591
- uischema: {},
1592
- schema: {}
1593
- },
1594
- setup(__props) {
1595
- const props = __props;
1596
- const { wrapper, value, onBlur, onChange } = useControlBinding(
1597
- props.uischema,
1598
- props.schema
1599
- );
1600
- return (_ctx, _cache) => {
1601
- return vue.openBlock(), vue.createBlock(vue.unref(ui.Input), vue.mergeProps(vue.unref(wrapper), {
1602
- modelValue: vue.unref(value),
1603
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.isRef(value) ? value.value = $event : null),
1604
- onBlur: vue.unref(onBlur),
1605
- onChange: vue.unref(onChange)
1606
- }), null, 16, ["modelValue", "onBlur", "onChange"]);
1607
- };
1608
- }
1609
- });
1610
- const _sfc_main$a = /* @__PURE__ */ vue.defineComponent({
1611
- __name: "TextAreaControlRenderer",
1612
- props: {
1613
- uischema: {},
1614
- schema: {}
1615
- },
1616
- setup(__props) {
1617
- const props = __props;
1618
- const { wrapper, value, onBlur, onChange, appliedOptions } = useControlBinding(
1619
- props.uischema,
1620
- props.schema
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
- });
1631
- return (_ctx, _cache) => {
1632
- return vue.openBlock(), vue.createBlock(vue.unref(ui.Textarea), vue.mergeProps(vue.unref(wrapper), {
1633
- modelValue: vue.unref(value),
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,
1638
- onBlur: vue.unref(onBlur),
1639
- onChange: vue.unref(onChange)
1640
- }), null, 16, ["modelValue", "dir", "height", "min-height", "onBlur", "onChange"]);
1641
- };
1642
- }
1643
- });
1644
- const controlRenderers = [
1645
- { tester: rankWith(10, isStringFormat), renderer: _sfc_main$b },
1646
- {
1647
- tester: rankWith(11, isTextAreaControl),
1648
- renderer: _sfc_main$a
1649
- },
1650
- {
1651
- tester: rankWith(11, isMarkdownControl),
1652
- renderer: _sfc_main$f
1653
- },
1654
- { tester: rankWith(11, isBooleanControl), renderer: _sfc_main$g },
1655
- { tester: rankWith(11, isSelectControl), renderer: _sfc_main$c },
1656
- {
1657
- tester: rankWith(11, isMultiselectControl),
1658
- renderer: _sfc_main$e
1659
- },
1660
- {
1661
- tester: rankWith(12, isAutoCompleteControl),
1662
- renderer: _sfc_main$h
1663
- },
1664
- {
1665
- tester: rankWith(12, isNumberFormat),
1666
- renderer: _sfc_main$d
1667
- },
1668
- {
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
1699
- }
1700
- ];
1701
- const COLSPAN = {
1702
- 1: "col-span-1",
1703
- 2: "col-span-2",
1704
- 3: "col-span-3",
1705
- 4: "col-span-4",
1706
- 5: "col-span-5",
1707
- 6: "col-span-6",
1708
- 7: "col-span-7",
1709
- 8: "col-span-8",
1710
- 9: "col-span-9",
1711
- 10: "col-span-10",
1712
- 11: "col-span-11",
1713
- 12: "col-span-12"
1714
- };
1715
- const _hoisted_1$7 = { class: "flex flex-col gap-4" };
1716
- const _sfc_main$8 = /* @__PURE__ */ vue.defineComponent({
1717
- __name: "CollapseLayoutRenderer",
1718
- props: {
1719
- uischema: {},
1720
- schema: {}
1721
- },
1722
- setup(__props) {
1723
- const props = __props;
1724
- const pathPrefix = vue.inject("pathPrefix", "");
1725
- const opts = props.uischema.options ?? {};
1726
- const titleKeyField = opts.titleKey ? veeValidate.useFieldValue(
1727
- () => pathPrefix ? `${pathPrefix}.${opts.titleKey}` : opts.titleKey
1728
- ) : void 0;
1729
- const title = vue.computed(() => {
1730
- if (titleKeyField?.value) return titleKeyField.value;
1731
- return props.uischema.label ?? opts.title ?? "Details";
1732
- });
1733
- return (_ctx, _cache) => {
1734
- return vue.openBlock(), vue.createBlock(vue.unref(ui.Collapse), { title: title.value }, {
1735
- default: vue.withCtx(() => [
1736
- vue.createElementVNode("div", _hoisted_1$7, [
1737
- (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.uischema.elements, (child, i) => {
1738
- return vue.openBlock(), vue.createElementBlock("div", {
1739
- key: i,
1740
- class: vue.normalizeClass(vue.unref(COLSPAN)[child.options?.colspan ?? 12])
1741
- }, [
1742
- vue.createVNode(_sfc_main$5, {
1743
- uischema: child,
1744
- schema: __props.schema
1745
- }, null, 8, ["uischema", "schema"])
1746
- ], 2);
1747
- }), 128))
1748
- ])
1749
- ]),
1750
- _: 1
1751
- }, 8, ["title"]);
1752
- };
1753
- }
1754
- });
1755
- const _hoisted_1$6 = {
1756
- key: 1,
1757
- class: "flex flex-col gap-3"
1758
- };
1759
- const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
1760
- __name: "LayoutRenderer",
1761
- props: {
1762
- uischema: {},
1763
- schema: {}
1764
- },
1765
- setup(__props) {
1766
- const props = __props;
1767
- const LAYOUT = {
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",
1773
- VerticalLayout: "flex flex-col gap-3"
1774
- };
1775
- const getLayout = vue.computed(() => LAYOUT[props.uischema.type]);
1776
- const isLayout = vue.computed(() => props.uischema.type in LAYOUT);
1777
- return (_ctx, _cache) => {
1778
- return isLayout.value ? (vue.openBlock(), vue.createElementBlock("div", {
1779
- key: 0,
1780
- class: vue.normalizeClass(getLayout.value)
1781
- }, [
1782
- (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(__props.uischema.elements, (child, i) => {
1783
- return vue.openBlock(), vue.createElementBlock("div", {
1784
- key: i,
1785
- class: vue.normalizeClass(vue.unref(COLSPAN)[child.options?.colspan ?? 12])
1786
- }, [
1787
- vue.createVNode(_sfc_main$5, {
1788
- uischema: child,
1789
- schema: __props.schema
1790
- }, null, 8, ["uischema", "schema"])
1791
- ], 2);
1792
- }), 128))
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));
1908
- };
1909
- }
1910
- });
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
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"
1953
- ];
1954
- const _hoisted_1$3 = ["id"];
1955
- const _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
1956
- __name: "FormComponent",
1957
- props: JsonFormComponentProperties,
1958
- emits: JsonFormComponentEmits,
1959
- setup(__props, { expose: __expose, emit: __emit }) {
1960
- registerZodErrorMap();
1961
- const properties = __props;
1962
- const emits = __emit;
1963
- const zodSchema = vue.computed(() => {
1964
- if (!properties.schema) return void 0;
1965
- try {
1966
- const patched = jsonFormsCore.enforceRequiredStringMinLength(properties.schema);
1967
- return zod.fromJSONSchema(patched);
1968
- } catch {
1969
- return void 0;
1970
- }
1971
- });
1972
- const { values, errors, meta, setValues, validate } = veeValidate.useForm({
1973
- validationSchema: zodSchema,
1974
- initialValues: properties.formData
1975
- });
1976
- vue.provide(
1977
- "renderers",
1978
- properties.renderers?.length ? [...customRenderers, ...properties.renderers] : customRenderers
1979
- );
1980
- vue.provide("readonlyRenderers", properties.renderers ?? []);
1981
- vue.provide("rootSchema", properties.schema);
1982
- vue.provide("styles", ui.myStyles);
1983
- const submitted = vue.ref(false);
1984
- vue.provide(ERROR_MODE_KEY, vue.toRef(properties, "errorMode"));
1985
- vue.provide(FORM_SUBMITTED_KEY, submitted);
1986
- vue.provide(FORM_READONLY_KEY, vue.toRef(properties, "readonly"));
1987
- vue.onMounted(async () => {
1988
- const result = await validate();
1989
- emits("valid", result.valid);
1990
- });
1991
- provideFormEvents((payload) => {
1992
- emits("events", payload);
1993
- });
1994
- if (properties.http) {
1995
- provideHttpClient(properties.http);
1996
- }
1997
- let syncing = false;
1998
- vue.watch(
1999
- () => properties.formData,
2000
- (newData) => {
2001
- if (!newData) return;
2002
- if (JSON.stringify(newData) === JSON.stringify(vue.toRaw(values))) return;
2003
- syncing = true;
2004
- setValues(newData);
2005
- vue.nextTick(() => {
2006
- syncing = false;
2007
- });
2008
- },
2009
- { deep: true }
2010
- );
2011
- vue.watch(
2012
- values,
2013
- (newValues) => {
2014
- if (syncing) return;
2015
- const isValid = meta.value.valid;
2016
- emits("valid", isValid);
2017
- emits("change", vue.toRaw(newValues));
2018
- },
2019
- { deep: true }
2020
- );
2021
- vue.watch(
2022
- errors,
2023
- (newErrors) => {
2024
- const errorList = Object.entries(newErrors).filter(([, msg]) => !!msg).map(([path, message]) => ({ path, message }));
2025
- emits("errors", errorList);
2026
- },
2027
- { deep: true }
2028
- );
2029
- const onSubmit = () => {
2030
- submitted.value = true;
2031
- emits("submit", {
2032
- data: vue.toRaw(values),
2033
- valid: meta.value.valid
2034
- });
2035
- };
2036
- const markSubmitted = () => {
2037
- submitted.value = true;
2038
- };
2039
- __expose({ markSubmitted });
2040
- return (_ctx, _cache) => {
2041
- return vue.openBlock(), vue.createElementBlock("form", {
2042
- id: _ctx.id,
2043
- onSubmit: vue.withModifiers(onSubmit, ["prevent"])
2044
- }, [
2045
- vue.createVNode(_sfc_main$5, {
2046
- uischema: _ctx.uiSchema,
2047
- schema: _ctx.schema
2048
- }, null, 8, ["uischema", "schema"])
2049
- ], 40, _hoisted_1$3);
2050
- };
2051
- }
2052
- });
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}`);
2073
- });
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) => {
2085
- return response.data;
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}`);
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
- }
2139
- }
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,
2150
- setup(__props, { emit: __emit }) {
2151
- const properties = __props;
2152
- const emits = __emit;
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
- })
2172
- );
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"]);
2182
- };
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 });
2203
- };
2204
- const onOperatorChange = (op) => {
2205
- emit("update:modelValue", {
2206
- ...props.modelValue,
2207
- operator: op,
2208
- value: jsonFormsCore.OperatorNoValue.has(op) ? "" : props.modelValue.value
2209
- });
2210
- };
2211
- return (_ctx, _cache) => {
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
- ]);
2245
- };
2246
- }
2247
- });
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"
2252
- };
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"],
2267
- setup(__props, { emit: __emit }) {
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;
2282
- const emit = __emit;
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
- }));
2290
- });
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
- ];
2312
- };
2313
- const removeRow = (index) => {
2314
- const updated = rows.value.filter((_, i) => i !== index);
2315
- rows.value = updated.length > 0 ? updated : [emptyRow()];
2316
- };
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;
2328
- };
2329
- return (_ctx, _cache) => {
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, [
2368
- vue.createVNode(vue.unref(ui.Btn), {
2369
- icon: vue.unref(ui.IconEnum).Plus,
2370
- color: "ghost",
2371
- onClick: addRow
2372
- }, {
2373
- default: vue.withCtx(() => [..._cache[2] || (_cache[2] = [
2374
- vue.createTextVNode(" Add filter ", -1)
2375
- ])]),
2376
- _: 1
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
- ])
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)
2440
- ]),
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
- ]);
2481
- };
2482
- }
2483
- });
2484
- exports.FilterRowInput = _sfc_main$2;
2485
- exports.FormModalEmits = FormModalEmits;
2486
- exports.FormModalProperties = FormModalProperties;
2487
- exports.JsonForm = _sfc_main$4;
2488
- exports.JsonFormModal = _sfc_main$j;
2489
- exports.JsonFormModalService = JsonFormModalService;
2490
- exports.ReadonlyLabel = _sfc_main$u;
2491
- exports.TableComponent = _sfc_main$3;
2492
- exports.TableComponentEmits = TableComponentEmits;
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;
2499
- exports.createRepository = createRepository;
2500
- exports.customRenderers = customRenderers;
2501
- exports.defaultCellRenderers = defaultCellRenderers;
2502
- exports.findCellRenderer = findCellRenderer;
2503
- exports.formatError = formatError;
2504
- exports.getNestedValue = getNestedValue;
2505
- exports.isLink = isLink;
2506
- exports.optionIsIgnoreCase = optionIsIgnoreCase;
2507
- exports.provideFormEvents = provideFormEvents;
2508
- exports.provideHttpClient = provideHttpClient;
2509
- exports.readonlyRenderers = readonlyRenderers;
2510
- exports.registerZodErrorMap = registerZodErrorMap;
2511
- exports.useControlBinding = useControlBinding;
2512
- exports.useCustomControlBinding = useCustomControlBinding;
2513
- exports.useCustomReadonlyControlBinding = useCustomReadonlyControlBinding;
2514
- exports.useDisplayValue = useDisplayValue;
2515
- exports.useFormEvents = useFormEvents;
2516
- exports.useHttpClient = useHttpClient;
2517
- exports.useReadonlyControlBinding = useReadonlyControlBinding;