@narrative.io/jsonforms-provider-protocols 1.1.0-beta.0 → 1.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jsonforms-provider-protocols.css +4 -0
- package/dist/vue/components/ProviderMultiSelect.vue.d.ts +9 -0
- package/dist/vue/components/ProviderMultiSelect.vue.d.ts.map +1 -0
- package/dist/vue/components/ProviderMultiSelect.vue.js +8 -0
- package/dist/vue/components/ProviderMultiSelect.vue.js.map +1 -0
- package/dist/vue/components/ProviderMultiSelect.vue2.js +95 -0
- package/dist/vue/components/ProviderMultiSelect.vue2.js.map +1 -0
- package/dist/vue/index.d.ts +2 -1
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +20 -2
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.js +35 -3
- package/dist/vue/primevue/JfNumber.vue.js.map +1 -1
- package/package.json +1 -1
- package/src/vue/components/ProviderMultiSelect.vue +108 -0
- package/src/vue/index.ts +26 -2
- package/src/vue/primevue/JfNumber.vue +44 -4
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ControlElement, JsonSchema } from "@jsonforms/core";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
uischema: ControlElement;
|
|
4
|
+
schema: JsonSchema;
|
|
5
|
+
path: string;
|
|
6
|
+
};
|
|
7
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
export default _default;
|
|
9
|
+
//# sourceMappingURL=ProviderMultiSelect.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProviderMultiSelect.vue.d.ts","sourceRoot":"","sources":["../../../src/vue/components/ProviderMultiSelect.vue"],"names":[],"mappings":"AA8GA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMlE,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,cAAc,CAAC;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;;AAkLF,wBAEG"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import _sfc_main from "./ProviderMultiSelect.vue2.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import _export_sfc from "../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
+
const ProviderMultiSelect = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-259a0a08"]]);
|
|
5
|
+
export {
|
|
6
|
+
ProviderMultiSelect as default
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=ProviderMultiSelect.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProviderMultiSelect.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { defineComponent, computed, inject, createElementBlock, openBlock, createCommentVNode, createVNode, unref, toDisplayString } from "vue";
|
|
2
|
+
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
3
|
+
import { useProvider } from "../composables/useProvider.js";
|
|
4
|
+
import MultiSelect from "primevue/multiselect";
|
|
5
|
+
const _hoisted_1 = { class: "flex flex-column gap-2" };
|
|
6
|
+
const _hoisted_2 = {
|
|
7
|
+
key: 0,
|
|
8
|
+
class: "text-color text-left"
|
|
9
|
+
};
|
|
10
|
+
const _hoisted_3 = {
|
|
11
|
+
key: 1,
|
|
12
|
+
class: "text-color-secondary text-left"
|
|
13
|
+
};
|
|
14
|
+
const _hoisted_4 = {
|
|
15
|
+
key: 2,
|
|
16
|
+
class: "p-error",
|
|
17
|
+
role: "alert"
|
|
18
|
+
};
|
|
19
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
20
|
+
__name: "ProviderMultiSelect",
|
|
21
|
+
props: {
|
|
22
|
+
uischema: {},
|
|
23
|
+
schema: {},
|
|
24
|
+
path: {}
|
|
25
|
+
},
|
|
26
|
+
setup(__props) {
|
|
27
|
+
const props = __props;
|
|
28
|
+
const { control, handleChange } = useJsonFormsControl(props);
|
|
29
|
+
const binding = computed(() => {
|
|
30
|
+
const provider = control.value.uischema?.options?.provider;
|
|
31
|
+
if (provider && typeof provider === "object" && !provider.load) {
|
|
32
|
+
return { ...provider, load: "mount" };
|
|
33
|
+
}
|
|
34
|
+
return provider;
|
|
35
|
+
});
|
|
36
|
+
const deps = computed(
|
|
37
|
+
() => control.value.schema?.["x-provider"]?.dependsOn ?? []
|
|
38
|
+
);
|
|
39
|
+
const depValues = computed(() => deps.value.map(() => null));
|
|
40
|
+
const injectedFormData = inject("formData", { value: {} });
|
|
41
|
+
const rootData = computed(() => injectedFormData.value || {});
|
|
42
|
+
const { items, loading, error } = useProvider(binding, {
|
|
43
|
+
data: rootData,
|
|
44
|
+
// Pass the reactive reference
|
|
45
|
+
path: control.value.path,
|
|
46
|
+
dependsOnValues: depValues.value
|
|
47
|
+
});
|
|
48
|
+
const sameSet = (a, b) => {
|
|
49
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
|
|
50
|
+
return false;
|
|
51
|
+
const s = new Set(b);
|
|
52
|
+
return a.every((v) => s.has(v));
|
|
53
|
+
};
|
|
54
|
+
const value = computed({
|
|
55
|
+
get() {
|
|
56
|
+
const curr = Array.isArray(control.value.data) ? control.value.data : [];
|
|
57
|
+
return [...curr];
|
|
58
|
+
},
|
|
59
|
+
set(val) {
|
|
60
|
+
const next = Array.isArray(val) ? [...val] : [];
|
|
61
|
+
const curr = Array.isArray(control.value.data) ? control.value.data : [];
|
|
62
|
+
if (!sameSet(curr, next)) handleChange(control.value.path, next);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
const placeholder = computed(() => {
|
|
66
|
+
if (loading.value) return "Loading…";
|
|
67
|
+
const uischemaPlaceholder = control.value.uischema?.options?.placeholder;
|
|
68
|
+
return uischemaPlaceholder || "Select…";
|
|
69
|
+
});
|
|
70
|
+
return (_ctx, _cache) => {
|
|
71
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
72
|
+
unref(control).schema.title ? (openBlock(), createElementBlock("label", _hoisted_2, toDisplayString(unref(control).schema.title), 1)) : createCommentVNode("", true),
|
|
73
|
+
unref(control).description ? (openBlock(), createElementBlock("div", _hoisted_3, toDisplayString(unref(control).description), 1)) : createCommentVNode("", true),
|
|
74
|
+
createVNode(unref(MultiSelect), {
|
|
75
|
+
modelValue: value.value,
|
|
76
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => value.value = $event),
|
|
77
|
+
class: "w-full",
|
|
78
|
+
options: unref(items),
|
|
79
|
+
"option-label": "label",
|
|
80
|
+
"option-value": "value",
|
|
81
|
+
"data-key": "value",
|
|
82
|
+
display: "chip",
|
|
83
|
+
placeholder: placeholder.value,
|
|
84
|
+
disabled: !unref(control).enabled || unref(loading),
|
|
85
|
+
"show-clear": true
|
|
86
|
+
}, null, 8, ["modelValue", "options", "placeholder", "disabled"]),
|
|
87
|
+
unref(error) ? (openBlock(), createElementBlock("small", _hoisted_4, "Failed to load: " + toDisplayString(unref(error)), 1)) : createCommentVNode("", true)
|
|
88
|
+
]);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
export {
|
|
93
|
+
_sfc_main as default
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=ProviderMultiSelect.vue2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProviderMultiSelect.vue2.js","sources":["../../../src/vue/components/ProviderMultiSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ControlElement, JsonSchema } from \"@jsonforms/core\";\nimport { useJsonFormsControl } from \"@jsonforms/vue\";\nimport { computed, inject } from \"vue\";\nimport { useProvider } from \"../composables/useProvider\";\nimport MultiSelect from \"primevue/multiselect\";\n\nconst props = defineProps<{\n uischema: ControlElement;\n schema: JsonSchema;\n path: string;\n}>();\nconst { control, handleChange } = useJsonFormsControl(props);\n\nconst binding = computed(() => {\n const provider = control.value.uischema?.options?.provider;\n // Ensure load property is set to 'mount' by default\n if (provider && typeof provider === \"object\" && !provider.load) {\n return { ...provider, load: \"mount\" };\n }\n return provider;\n});\n\nconst deps = computed(\n () =>\n ((\n (control.value.schema as Record<string, unknown>)?.[\n \"x-provider\"\n ] as Record<string, unknown>\n )?.dependsOn as string[]) ?? [],\n);\nconst depValues = computed(() => deps.value.map(() => null)); // you can resolve actual values via control.value.data & pointers\n\n// Get the root form data from JSONForms context for template URL resolution\nconst injectedFormData = inject<{ value: unknown }>(\"formData\", { value: {} });\nconst rootData = computed(() => injectedFormData.value || {});\n\nconst { items, loading, error } = useProvider(binding, {\n data: rootData, // Pass the reactive reference\n path: control.value.path,\n dependsOnValues: depValues.value,\n});\n\n// Provider will automatically reload when rootData changes due to reactive cache key\n\n// order-insensitive shallow equality for primitive arrays\nconst sameSet = (a: unknown[], b: unknown[]) => {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)\n return false;\n const s = new Set(b);\n return a.every((v) => s.has(v));\n};\n\n// v-model with guard to avoid recursive updates\nconst value = computed({\n get() {\n const curr = Array.isArray(control.value.data) ? control.value.data : [];\n // return a fresh copy so MultiSelect can't mutate JSONForms' array in place\n return [...curr];\n },\n set(val) {\n const next = Array.isArray(val) ? [...val] : [];\n const curr = Array.isArray(control.value.data) ? control.value.data : [];\n if (!sameSet(curr, next)) handleChange(control.value.path, next);\n },\n});\n\nconst placeholder = computed(() => {\n if (loading.value) return \"Loading…\";\n // Check for placeholder in uischema options\n const uischemaPlaceholder = (\n control.value.uischema as { options?: { placeholder?: string } }\n )?.options?.placeholder;\n return uischemaPlaceholder || \"Select…\";\n});\n</script>\n\n<template>\n <div class=\"flex flex-column gap-2\">\n <label v-if=\"control.schema.title\" class=\"text-color text-left\">{{\n control.schema.title\n }}</label>\n <div v-if=\"control.description\" class=\"text-color-secondary text-left\">\n {{ control.description }}\n </div>\n <MultiSelect\n v-model=\"value\"\n class=\"w-full\"\n :options=\"items\"\n option-label=\"label\"\n option-value=\"value\"\n data-key=\"value\"\n display=\"chip\"\n :placeholder=\"placeholder\"\n :disabled=\"!control.enabled || loading\"\n :show-clear=\"true\"\n />\n <small v-if=\"error\" class=\"p-error\" role=\"alert\"\n >Failed to load: {{ error }}</small\n >\n </div>\n</template>\n\n<style scoped>\n:deep(.p-multiselect-label) {\n text-align: left;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_unref","_toDisplayString","_createVNode"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,UAAM,QAAQ;AAKd,UAAM,EAAE,SAAS,iBAAiB,oBAAoB,KAAK;AAE3D,UAAM,UAAU,SAAS,MAAM;AAC7B,YAAM,WAAW,QAAQ,MAAM,UAAU,SAAS;AAElD,UAAI,YAAY,OAAO,aAAa,YAAY,CAAC,SAAS,MAAM;AAC9D,eAAO,EAAE,GAAG,UAAU,MAAM,QAAA;AAAA,MAC9B;AACA,aAAO;AAAA,IACT,CAAC;AAED,UAAM,OAAO;AAAA,MACX,MAEK,QAAQ,MAAM,SACb,YACF,GACC,aAA0B,CAAA;AAAA,IAAC;AAElC,UAAM,YAAY,SAAS,MAAM,KAAK,MAAM,IAAI,MAAM,IAAI,CAAC;AAG3D,UAAM,mBAAmB,OAA2B,YAAY,EAAE,OAAO,CAAA,GAAI;AAC7E,UAAM,WAAW,SAAS,MAAM,iBAAiB,SAAS,CAAA,CAAE;AAE5D,UAAM,EAAE,OAAO,SAAS,MAAA,IAAU,YAAY,SAAS;AAAA,MACrD,MAAM;AAAA;AAAA,MACN,MAAM,QAAQ,MAAM;AAAA,MACpB,iBAAiB,UAAU;AAAA,IAAA,CAC5B;AAKD,UAAM,UAAU,CAAC,GAAc,MAAiB;AAC9C,UAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE;AAC3D,eAAO;AACT,YAAM,IAAI,IAAI,IAAI,CAAC;AACnB,aAAO,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAAA,IAChC;AAGA,UAAM,QAAQ,SAAS;AAAA,MACrB,MAAM;AACJ,cAAM,OAAO,MAAM,QAAQ,QAAQ,MAAM,IAAI,IAAI,QAAQ,MAAM,OAAO,CAAA;AAEtE,eAAO,CAAC,GAAG,IAAI;AAAA,MACjB;AAAA,MACA,IAAI,KAAK;AACP,cAAM,OAAO,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAA;AAC7C,cAAM,OAAO,MAAM,QAAQ,QAAQ,MAAM,IAAI,IAAI,QAAQ,MAAM,OAAO,CAAA;AACtE,YAAI,CAAC,QAAQ,MAAM,IAAI,EAAG,cAAa,QAAQ,MAAM,MAAM,IAAI;AAAA,MACjE;AAAA,IAAA,CACD;AAED,UAAM,cAAc,SAAS,MAAM;AACjC,UAAI,QAAQ,MAAO,QAAO;AAE1B,YAAM,sBACJ,QAAQ,MAAM,UACb,SAAS;AACZ,aAAO,uBAAuB;AAAA,IAChC,CAAC;;AAIC,aAAAA,UAAA,GAAAC,mBAsBM,OAtBN,YAsBM;AAAA,QArBSC,MAAA,OAAA,EAAQ,OAAO,SAA5BF,aAAAC,mBAEU,SAFV,YAEUE,gBADRD,eAAQ,OAAO,KAAK,GAAA,CAAA;QAEXA,MAAA,OAAA,EAAQ,eAAnBF,UAAA,GAAAC,mBAEM,OAFN,YAEME,gBADDD,MAAA,OAAA,EAAQ,WAAW,GAAA,CAAA;QAExBE,YAWEF,MAAA,WAAA,GAAA;AAAA,sBAVS,MAAA;AAAA,uEAAA,MAAK,QAAA;AAAA,UACd,OAAM;AAAA,UACL,SAASA,MAAA,KAAA;AAAA,UACV,gBAAa;AAAA,UACb,gBAAa;AAAA,UACb,YAAS;AAAA,UACT,SAAQ;AAAA,UACP,aAAa,YAAA;AAAA,UACb,UAAQ,CAAGA,MAAA,OAAA,EAAQ,WAAWA,MAAA,OAAA;AAAA,UAC9B,cAAY;AAAA,QAAA;QAEFA,MAAA,KAAA,KAAbF,aAAAC,mBAEC,SAFD,YACG,qCAAmBC,MAAA,KAAA,CAAK,GAAA,CAAA;;;;;"}
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { UISchemaElement } from "@jsonforms/core";
|
|
2
2
|
import ProviderAutocomplete from "./components/ProviderAutocomplete.vue";
|
|
3
3
|
import ProviderSelect from "./components/ProviderSelect.vue";
|
|
4
|
+
import ProviderMultiSelect from "./components/ProviderMultiSelect.vue";
|
|
4
5
|
export declare const providerRenderers: {
|
|
5
6
|
tester: (uischema: UISchemaElement, schema: import("@jsonforms/core").JsonSchema, context: import("@jsonforms/core").TesterContext) => number;
|
|
6
7
|
renderer: import("vue").DefineComponent<{
|
|
@@ -14,7 +15,7 @@ export declare const providerRenderers: {
|
|
|
14
15
|
}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
16
|
}[];
|
|
16
17
|
export { primevueRenderers } from "./primevue";
|
|
17
|
-
export { ProviderAutocomplete, ProviderSelect };
|
|
18
|
+
export { ProviderAutocomplete, ProviderSelect, ProviderMultiSelect };
|
|
18
19
|
export { useProvider } from "./composables/useProvider";
|
|
19
20
|
export * from "./testers";
|
|
20
21
|
export * from "./primevue";
|
package/dist/vue/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vue/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AASvD,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AACzE,OAAO,cAAc,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vue/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AASvD,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AACzE,OAAO,cAAc,MAAM,iCAAiC,CAAC;AAC7D,OAAO,mBAAmB,MAAM,sCAAsC,CAAC;AAgDvE,eAAO,MAAM,iBAAiB;;;;;;;;;;;GAI7B,CAAC;AAGF,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
package/dist/vue/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { rankWith, and, or, isStringControl, isNumberControl, isIntegerControl } from "@jsonforms/core";
|
|
2
2
|
import _sfc_main from "./components/ProviderAutocomplete.vue.js";
|
|
3
3
|
import ProviderSelect from "./components/ProviderSelect.vue.js";
|
|
4
|
+
import ProviderMultiSelect from "./components/ProviderMultiSelect.vue.js";
|
|
4
5
|
import { primevueRenderers } from "./primevue/index.js";
|
|
5
6
|
import { useProvider } from "./composables/useProvider.js";
|
|
6
7
|
import { providerTester } from "./testers.js";
|
|
@@ -31,9 +32,25 @@ const providerAutocompleteTester = rankWith(
|
|
|
31
32
|
(uischema) => uischema?.options?.autocomplete === true
|
|
32
33
|
)
|
|
33
34
|
);
|
|
35
|
+
const isArrayControl = (uischema, schema) => {
|
|
36
|
+
const controlSchema = uischema;
|
|
37
|
+
if (controlSchema.type !== "Control" || !controlSchema.scope) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const rootSchema = schema;
|
|
41
|
+
const propertyPath = controlSchema.scope.replace("#/properties/", "");
|
|
42
|
+
const propertySchema = rootSchema?.properties?.[propertyPath];
|
|
43
|
+
return propertySchema?.type === "array";
|
|
44
|
+
};
|
|
45
|
+
const providerMultiSelectTester = rankWith(
|
|
46
|
+
108,
|
|
47
|
+
// Highest priority for array controls with providers
|
|
48
|
+
and(isArrayControl, hasProvider)
|
|
49
|
+
);
|
|
34
50
|
const providerRenderers = [
|
|
35
|
-
{ tester:
|
|
36
|
-
{ tester: providerAutocompleteTester, renderer: _sfc_main }
|
|
51
|
+
{ tester: providerMultiSelectTester, renderer: ProviderMultiSelect },
|
|
52
|
+
{ tester: providerAutocompleteTester, renderer: _sfc_main },
|
|
53
|
+
{ tester: providerSelectTester, renderer: ProviderSelect }
|
|
37
54
|
];
|
|
38
55
|
export {
|
|
39
56
|
default6 as JfBoolean,
|
|
@@ -43,6 +60,7 @@ export {
|
|
|
43
60
|
default2 as JfText,
|
|
44
61
|
default3 as JfTextArea,
|
|
45
62
|
_sfc_main as ProviderAutocomplete,
|
|
63
|
+
ProviderMultiSelect,
|
|
46
64
|
ProviderSelect,
|
|
47
65
|
primevueRenderers,
|
|
48
66
|
providerRenderers,
|
package/dist/vue/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/vue/index.ts"],"sourcesContent":["import type { UISchemaElement } from \"@jsonforms/core\";\nimport {\n and,\n isIntegerControl,\n isNumberControl,\n isStringControl,\n or,\n rankWith,\n} from \"@jsonforms/core\";\nimport ProviderAutocomplete from \"./components/ProviderAutocomplete.vue\";\nimport ProviderSelect from \"./components/ProviderSelect.vue\";\n\n// Custom tester that checks if provider option exists (as object or boolean)\nconst hasProvider = (uischema: UISchemaElement) => {\n return uischema?.options?.provider !== undefined;\n};\n\n// Create specific testers for each component type\nconst providerSelectTester = rankWith(\n 106, // Higher than PrimeVue base (100) to ensure providers take precedence\n and(\n or(isStringControl, isNumberControl, isIntegerControl),\n hasProvider,\n (uischema) => !uischema?.options?.autocomplete,\n ),\n);\n\nconst providerAutocompleteTester = rankWith(\n 107, // Higher than PrimeVue base (100) to ensure providers take precedence\n and(\n or(isStringControl, isNumberControl, isIntegerControl),\n hasProvider,\n (uischema) => uischema?.options?.autocomplete === true,\n ),\n);\n\nexport const providerRenderers = [\n { tester:
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/vue/index.ts"],"sourcesContent":["import type { UISchemaElement } from \"@jsonforms/core\";\nimport {\n and,\n isIntegerControl,\n isNumberControl,\n isStringControl,\n or,\n rankWith,\n} from \"@jsonforms/core\";\nimport ProviderAutocomplete from \"./components/ProviderAutocomplete.vue\";\nimport ProviderSelect from \"./components/ProviderSelect.vue\";\nimport ProviderMultiSelect from \"./components/ProviderMultiSelect.vue\";\n\n// Custom tester that checks if provider option exists (as object or boolean)\nconst hasProvider = (uischema: UISchemaElement) => {\n return uischema?.options?.provider !== undefined;\n};\n\n// Create specific testers for each component type\nconst providerSelectTester = rankWith(\n 106, // Higher than PrimeVue base (100) to ensure providers take precedence\n and(\n or(isStringControl, isNumberControl, isIntegerControl),\n hasProvider,\n (uischema) => !uischema?.options?.autocomplete,\n ),\n);\n\nconst providerAutocompleteTester = rankWith(\n 107, // Higher than PrimeVue base (100) to ensure providers take precedence\n and(\n or(isStringControl, isNumberControl, isIntegerControl),\n hasProvider,\n (uischema) => uischema?.options?.autocomplete === true,\n ),\n);\n\n// Custom array tester - check both uischema control type and schema type\nconst isArrayControl = (uischema: UISchemaElement, schema: unknown) => {\n const controlSchema = uischema as { type: string; scope?: string };\n if (controlSchema.type !== \"Control\" || !controlSchema.scope) {\n return false;\n }\n\n // Extract the property schema from the root schema\n const rootSchema = schema as { properties?: Record<string, unknown> };\n const propertyPath = controlSchema.scope.replace(\"#/properties/\", \"\");\n const propertySchema = rootSchema?.properties?.[propertyPath] as {\n type?: string;\n };\n\n return propertySchema?.type === \"array\";\n};\n\nconst providerMultiSelectTester = rankWith(\n 108, // Highest priority for array controls with providers\n and(isArrayControl, hasProvider),\n);\n\nexport const providerRenderers = [\n { tester: providerMultiSelectTester, renderer: ProviderMultiSelect },\n { tester: providerAutocompleteTester, renderer: ProviderAutocomplete },\n { tester: providerSelectTester, renderer: ProviderSelect },\n];\n\n// Export PrimeVue renderers (styles are auto-injected)\nexport { primevueRenderers } from \"./primevue\";\n\n// Export individual components\nexport { ProviderAutocomplete, ProviderSelect, ProviderMultiSelect };\nexport { useProvider } from \"./composables/useProvider\";\nexport * from \"./testers\";\nexport * from \"./primevue\";\n"],"names":["ProviderAutocomplete"],"mappings":";;;;;;;;;;;;;AAcA,MAAM,cAAc,CAAC,aAA8B;AACjD,SAAO,UAAU,SAAS,aAAa;AACzC;AAGA,MAAM,uBAAuB;AAAA,EAC3B;AAAA;AAAA,EACA;AAAA,IACE,GAAG,iBAAiB,iBAAiB,gBAAgB;AAAA,IACrD;AAAA,IACA,CAAC,aAAa,CAAC,UAAU,SAAS;AAAA,EAAA;AAEtC;AAEA,MAAM,6BAA6B;AAAA,EACjC;AAAA;AAAA,EACA;AAAA,IACE,GAAG,iBAAiB,iBAAiB,gBAAgB;AAAA,IACrD;AAAA,IACA,CAAC,aAAa,UAAU,SAAS,iBAAiB;AAAA,EAAA;AAEtD;AAGA,MAAM,iBAAiB,CAAC,UAA2B,WAAoB;AACrE,QAAM,gBAAgB;AACtB,MAAI,cAAc,SAAS,aAAa,CAAC,cAAc,OAAO;AAC5D,WAAO;AAAA,EACT;AAGA,QAAM,aAAa;AACnB,QAAM,eAAe,cAAc,MAAM,QAAQ,iBAAiB,EAAE;AACpE,QAAM,iBAAiB,YAAY,aAAa,YAAY;AAI5D,SAAO,gBAAgB,SAAS;AAClC;AAEA,MAAM,4BAA4B;AAAA,EAChC;AAAA;AAAA,EACA,IAAI,gBAAgB,WAAW;AACjC;AAEO,MAAM,oBAAoB;AAAA,EAC/B,EAAE,QAAQ,2BAA2B,UAAU,oBAAA;AAAA,EAC/C,EAAE,QAAQ,4BAA4B,UAAUA,UAAA;AAAA,EAChD,EAAE,QAAQ,sBAAsB,UAAU,eAAA;AAC5C;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JfNumber.vue.d.ts","sourceRoot":"","sources":["../../../src/vue/primevue/JfNumber.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"JfNumber.vue.d.ts","sourceRoot":"","sources":["../../../src/vue/primevue/JfNumber.vue"],"names":[],"mappings":"AAoFA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8KtD,wBAEG"}
|
|
@@ -21,9 +21,37 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
21
21
|
setup(__props) {
|
|
22
22
|
const props = __props;
|
|
23
23
|
const { control, handleChange } = useJsonFormsControl(props);
|
|
24
|
+
const options = computed(
|
|
25
|
+
() => control.value.uischema?.options ?? {}
|
|
26
|
+
);
|
|
24
27
|
const placeholder = computed(
|
|
25
|
-
() =>
|
|
28
|
+
() => options.value.placeholder ?? control.value.description
|
|
29
|
+
);
|
|
30
|
+
const mode = computed(() => {
|
|
31
|
+
if (options.value.currency) return "currency";
|
|
32
|
+
if (options.value.decimal || typeof options.value.precision === "number")
|
|
33
|
+
return "decimal";
|
|
34
|
+
return void 0;
|
|
35
|
+
});
|
|
36
|
+
const currency = computed(
|
|
37
|
+
() => typeof options.value.currency === "string" ? options.value.currency : "USD"
|
|
26
38
|
);
|
|
39
|
+
const minFractionDigits = computed(() => {
|
|
40
|
+
if (mode.value === "currency") return 2;
|
|
41
|
+
if (typeof options.value.precision === "number")
|
|
42
|
+
return options.value.precision;
|
|
43
|
+
return void 0;
|
|
44
|
+
});
|
|
45
|
+
const maxFractionDigits = computed(() => {
|
|
46
|
+
if (mode.value === "currency") return 2;
|
|
47
|
+
if (typeof options.value.precision === "number")
|
|
48
|
+
return options.value.precision;
|
|
49
|
+
return void 0;
|
|
50
|
+
});
|
|
51
|
+
const useGrouping = computed(() => {
|
|
52
|
+
if (mode.value === "currency") return true;
|
|
53
|
+
return options.value.useGrouping === true;
|
|
54
|
+
});
|
|
27
55
|
const onNumber = (val) => handleChange(control.value.path, val ?? void 0);
|
|
28
56
|
return (_ctx, _cache) => {
|
|
29
57
|
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
@@ -32,13 +60,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
32
60
|
createVNode(unref(InputNumber), {
|
|
33
61
|
class: "w-full",
|
|
34
62
|
"input-class": "w-full",
|
|
35
|
-
"use-grouping":
|
|
63
|
+
"use-grouping": useGrouping.value,
|
|
64
|
+
mode: mode.value,
|
|
65
|
+
currency: currency.value,
|
|
66
|
+
"min-fraction-digits": minFractionDigits.value,
|
|
67
|
+
"max-fraction-digits": maxFractionDigits.value,
|
|
36
68
|
"model-value": typeof unref(control).data === "number" ? unref(control).data : null,
|
|
37
69
|
placeholder: placeholder.value,
|
|
38
70
|
disabled: !unref(control).enabled,
|
|
39
71
|
"aria-invalid": !!unref(control).errors || void 0,
|
|
40
72
|
"onUpdate:modelValue": onNumber
|
|
41
|
-
}, null, 8, ["model-value", "placeholder", "disabled", "aria-invalid"]),
|
|
73
|
+
}, null, 8, ["use-grouping", "mode", "currency", "min-fraction-digits", "max-fraction-digits", "model-value", "placeholder", "disabled", "aria-invalid"]),
|
|
42
74
|
unref(control).errors ? (openBlock(), createElementBlock("small", _hoisted_4, toDisplayString(unref(control).errors), 1)) : createCommentVNode("", true)
|
|
43
75
|
]);
|
|
44
76
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JfNumber.vue.js","sources":["../../../src/vue/primevue/JfNumber.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ControlElement } from \"@jsonforms/core\";\nimport { rendererProps, useJsonFormsControl } from \"@jsonforms/vue\";\nimport { computed } from \"vue\";\nimport InputNumber from \"primevue/inputnumber\";\n\ndefineOptions({ name: \"JfNumber\" });\n\nconst props = defineProps(rendererProps<ControlElement>());\nconst { control, handleChange } = useJsonFormsControl(props);\n\nconst
|
|
1
|
+
{"version":3,"file":"JfNumber.vue.js","sources":["../../../src/vue/primevue/JfNumber.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ControlElement } from \"@jsonforms/core\";\nimport { rendererProps, useJsonFormsControl } from \"@jsonforms/vue\";\nimport { computed } from \"vue\";\nimport InputNumber from \"primevue/inputnumber\";\n\ndefineOptions({ name: \"JfNumber\" });\n\nconst props = defineProps(rendererProps<ControlElement>());\nconst { control, handleChange } = useJsonFormsControl(props);\n\nconst options = computed(\n () =>\n (control.value.uischema as { options?: Record<string, unknown> })\n ?.options ?? {},\n);\n\nconst placeholder = computed<string | undefined>(\n () => (options.value.placeholder as string) ?? control.value.description,\n);\n\n// Currency and decimal configuration\nconst mode = computed(() => {\n if (options.value.currency) return \"currency\";\n if (options.value.decimal || typeof options.value.precision === \"number\")\n return \"decimal\";\n return undefined;\n});\n\nconst currency = computed(() =>\n typeof options.value.currency === \"string\" ? options.value.currency : \"USD\",\n);\n\nconst minFractionDigits = computed(() => {\n if (mode.value === \"currency\") return 2;\n if (typeof options.value.precision === \"number\")\n return options.value.precision;\n return undefined;\n});\n\nconst maxFractionDigits = computed(() => {\n if (mode.value === \"currency\") return 2;\n if (typeof options.value.precision === \"number\")\n return options.value.precision;\n return undefined;\n});\n\nconst useGrouping = computed(() => {\n // Enable grouping for currency by default, or if explicitly set\n if (mode.value === \"currency\") return true;\n return options.value.useGrouping === true;\n});\n\nconst onNumber = (val: number | null) =>\n handleChange(control.value.path, val ?? undefined);\n</script>\n\n<template>\n <div class=\"flex flex-column gap-2\">\n <label v-if=\"control.label\" class=\"text-color text-left\">{{\n control.label\n }}</label>\n <div v-if=\"control.description\" class=\"text-color-secondary text-left\">\n {{ control.description }}\n </div>\n <InputNumber\n class=\"w-full\"\n input-class=\"w-full\"\n :use-grouping=\"useGrouping\"\n :mode=\"mode\"\n :currency=\"currency\"\n :min-fraction-digits=\"minFractionDigits\"\n :max-fraction-digits=\"maxFractionDigits\"\n :model-value=\"typeof control.data === 'number' ? control.data : null\"\n :placeholder=\"placeholder\"\n :disabled=\"!control.enabled\"\n :aria-invalid=\"!!control.errors || undefined\"\n @update:model-value=\"onNumber\"\n />\n <small v-if=\"control.errors\" class=\"p-error\">{{ control.errors }}</small>\n </div>\n</template>\n"],"names":["_openBlock","_createElementBlock","_unref","_toDisplayString","_createVNode"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAQA,UAAM,QAAQ;AACd,UAAM,EAAE,SAAS,iBAAiB,oBAAoB,KAAK;AAE3D,UAAM,UAAU;AAAA,MACd,MACG,QAAQ,MAAM,UACX,WAAW,CAAA;AAAA,IAAC;AAGpB,UAAM,cAAc;AAAA,MAClB,MAAO,QAAQ,MAAM,eAA0B,QAAQ,MAAM;AAAA,IAAA;AAI/D,UAAM,OAAO,SAAS,MAAM;AAC1B,UAAI,QAAQ,MAAM,SAAU,QAAO;AACnC,UAAI,QAAQ,MAAM,WAAW,OAAO,QAAQ,MAAM,cAAc;AAC9D,eAAO;AACT,aAAO;AAAA,IACT,CAAC;AAED,UAAM,WAAW;AAAA,MAAS,MACxB,OAAO,QAAQ,MAAM,aAAa,WAAW,QAAQ,MAAM,WAAW;AAAA,IAAA;AAGxE,UAAM,oBAAoB,SAAS,MAAM;AACvC,UAAI,KAAK,UAAU,WAAY,QAAO;AACtC,UAAI,OAAO,QAAQ,MAAM,cAAc;AACrC,eAAO,QAAQ,MAAM;AACvB,aAAO;AAAA,IACT,CAAC;AAED,UAAM,oBAAoB,SAAS,MAAM;AACvC,UAAI,KAAK,UAAU,WAAY,QAAO;AACtC,UAAI,OAAO,QAAQ,MAAM,cAAc;AACrC,eAAO,QAAQ,MAAM;AACvB,aAAO;AAAA,IACT,CAAC;AAED,UAAM,cAAc,SAAS,MAAM;AAEjC,UAAI,KAAK,UAAU,WAAY,QAAO;AACtC,aAAO,QAAQ,MAAM,gBAAgB;AAAA,IACvC,CAAC;AAED,UAAM,WAAW,CAAC,QAChB,aAAa,QAAQ,MAAM,MAAM,OAAO,MAAS;;AAIjD,aAAAA,UAAA,GAAAC,mBAsBM,OAtBN,YAsBM;AAAA,QArBSC,MAAA,OAAA,EAAQ,SAArBF,UAAA,GAAAC,mBAEU,SAFV,YAEUE,gBADRD,MAAA,OAAA,EAAQ,KAAK,GAAA,CAAA;QAEJA,MAAA,OAAA,EAAQ,eAAnBF,UAAA,GAAAC,mBAEM,OAFN,YAEME,gBADDD,MAAA,OAAA,EAAQ,WAAW,GAAA,CAAA;QAExBE,YAaEF,MAAA,WAAA,GAAA;AAAA,UAZA,OAAM;AAAA,UACN,eAAY;AAAA,UACX,gBAAc,YAAA;AAAA,UACd,MAAM,KAAA;AAAA,UACN,UAAU,SAAA;AAAA,UACV,uBAAqB,kBAAA;AAAA,UACrB,uBAAqB,kBAAA;AAAA,UACrB,sBAAoBA,MAAA,OAAA,EAAQ,SAAI,WAAgBA,MAAA,OAAA,EAAQ,OAAI;AAAA,UAC5D,aAAa,YAAA;AAAA,UACb,UAAQ,CAAGA,MAAA,OAAA,EAAQ;AAAA,UACnB,gBAAY,CAAA,CAAIA,MAAA,OAAA,EAAQ,UAAU;AAAA,UAClC,uBAAoB;AAAA,QAAA;QAEVA,MAAA,OAAA,EAAQ,UAArBF,UAAA,GAAAC,mBAAyE,SAAzE,YAAyEE,gBAAzBD,MAAA,OAAA,EAAQ,MAAM,GAAA,CAAA;;;;;"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ControlElement, JsonSchema } from "@jsonforms/core";
|
|
3
|
+
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
4
|
+
import { computed, inject } from "vue";
|
|
5
|
+
import { useProvider } from "../composables/useProvider";
|
|
6
|
+
import MultiSelect from "primevue/multiselect";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
uischema: ControlElement;
|
|
10
|
+
schema: JsonSchema;
|
|
11
|
+
path: string;
|
|
12
|
+
}>();
|
|
13
|
+
const { control, handleChange } = useJsonFormsControl(props);
|
|
14
|
+
|
|
15
|
+
const binding = computed(() => {
|
|
16
|
+
const provider = control.value.uischema?.options?.provider;
|
|
17
|
+
// Ensure load property is set to 'mount' by default
|
|
18
|
+
if (provider && typeof provider === "object" && !provider.load) {
|
|
19
|
+
return { ...provider, load: "mount" };
|
|
20
|
+
}
|
|
21
|
+
return provider;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const deps = computed(
|
|
25
|
+
() =>
|
|
26
|
+
((
|
|
27
|
+
(control.value.schema as Record<string, unknown>)?.[
|
|
28
|
+
"x-provider"
|
|
29
|
+
] as Record<string, unknown>
|
|
30
|
+
)?.dependsOn as string[]) ?? [],
|
|
31
|
+
);
|
|
32
|
+
const depValues = computed(() => deps.value.map(() => null)); // you can resolve actual values via control.value.data & pointers
|
|
33
|
+
|
|
34
|
+
// Get the root form data from JSONForms context for template URL resolution
|
|
35
|
+
const injectedFormData = inject<{ value: unknown }>("formData", { value: {} });
|
|
36
|
+
const rootData = computed(() => injectedFormData.value || {});
|
|
37
|
+
|
|
38
|
+
const { items, loading, error } = useProvider(binding, {
|
|
39
|
+
data: rootData, // Pass the reactive reference
|
|
40
|
+
path: control.value.path,
|
|
41
|
+
dependsOnValues: depValues.value,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Provider will automatically reload when rootData changes due to reactive cache key
|
|
45
|
+
|
|
46
|
+
// order-insensitive shallow equality for primitive arrays
|
|
47
|
+
const sameSet = (a: unknown[], b: unknown[]) => {
|
|
48
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
|
|
49
|
+
return false;
|
|
50
|
+
const s = new Set(b);
|
|
51
|
+
return a.every((v) => s.has(v));
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// v-model with guard to avoid recursive updates
|
|
55
|
+
const value = computed({
|
|
56
|
+
get() {
|
|
57
|
+
const curr = Array.isArray(control.value.data) ? control.value.data : [];
|
|
58
|
+
// return a fresh copy so MultiSelect can't mutate JSONForms' array in place
|
|
59
|
+
return [...curr];
|
|
60
|
+
},
|
|
61
|
+
set(val) {
|
|
62
|
+
const next = Array.isArray(val) ? [...val] : [];
|
|
63
|
+
const curr = Array.isArray(control.value.data) ? control.value.data : [];
|
|
64
|
+
if (!sameSet(curr, next)) handleChange(control.value.path, next);
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const placeholder = computed(() => {
|
|
69
|
+
if (loading.value) return "Loading…";
|
|
70
|
+
// Check for placeholder in uischema options
|
|
71
|
+
const uischemaPlaceholder = (
|
|
72
|
+
control.value.uischema as { options?: { placeholder?: string } }
|
|
73
|
+
)?.options?.placeholder;
|
|
74
|
+
return uischemaPlaceholder || "Select…";
|
|
75
|
+
});
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<template>
|
|
79
|
+
<div class="flex flex-column gap-2">
|
|
80
|
+
<label v-if="control.schema.title" class="text-color text-left">{{
|
|
81
|
+
control.schema.title
|
|
82
|
+
}}</label>
|
|
83
|
+
<div v-if="control.description" class="text-color-secondary text-left">
|
|
84
|
+
{{ control.description }}
|
|
85
|
+
</div>
|
|
86
|
+
<MultiSelect
|
|
87
|
+
v-model="value"
|
|
88
|
+
class="w-full"
|
|
89
|
+
:options="items"
|
|
90
|
+
option-label="label"
|
|
91
|
+
option-value="value"
|
|
92
|
+
data-key="value"
|
|
93
|
+
display="chip"
|
|
94
|
+
:placeholder="placeholder"
|
|
95
|
+
:disabled="!control.enabled || loading"
|
|
96
|
+
:show-clear="true"
|
|
97
|
+
/>
|
|
98
|
+
<small v-if="error" class="p-error" role="alert"
|
|
99
|
+
>Failed to load: {{ error }}</small
|
|
100
|
+
>
|
|
101
|
+
</div>
|
|
102
|
+
</template>
|
|
103
|
+
|
|
104
|
+
<style scoped>
|
|
105
|
+
:deep(.p-multiselect-label) {
|
|
106
|
+
text-align: left;
|
|
107
|
+
}
|
|
108
|
+
</style>
|
package/src/vue/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "@jsonforms/core";
|
|
10
10
|
import ProviderAutocomplete from "./components/ProviderAutocomplete.vue";
|
|
11
11
|
import ProviderSelect from "./components/ProviderSelect.vue";
|
|
12
|
+
import ProviderMultiSelect from "./components/ProviderMultiSelect.vue";
|
|
12
13
|
|
|
13
14
|
// Custom tester that checks if provider option exists (as object or boolean)
|
|
14
15
|
const hasProvider = (uischema: UISchemaElement) => {
|
|
@@ -34,16 +35,39 @@ const providerAutocompleteTester = rankWith(
|
|
|
34
35
|
),
|
|
35
36
|
);
|
|
36
37
|
|
|
38
|
+
// Custom array tester - check both uischema control type and schema type
|
|
39
|
+
const isArrayControl = (uischema: UISchemaElement, schema: unknown) => {
|
|
40
|
+
const controlSchema = uischema as { type: string; scope?: string };
|
|
41
|
+
if (controlSchema.type !== "Control" || !controlSchema.scope) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Extract the property schema from the root schema
|
|
46
|
+
const rootSchema = schema as { properties?: Record<string, unknown> };
|
|
47
|
+
const propertyPath = controlSchema.scope.replace("#/properties/", "");
|
|
48
|
+
const propertySchema = rootSchema?.properties?.[propertyPath] as {
|
|
49
|
+
type?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return propertySchema?.type === "array";
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const providerMultiSelectTester = rankWith(
|
|
56
|
+
108, // Highest priority for array controls with providers
|
|
57
|
+
and(isArrayControl, hasProvider),
|
|
58
|
+
);
|
|
59
|
+
|
|
37
60
|
export const providerRenderers = [
|
|
38
|
-
{ tester:
|
|
61
|
+
{ tester: providerMultiSelectTester, renderer: ProviderMultiSelect },
|
|
39
62
|
{ tester: providerAutocompleteTester, renderer: ProviderAutocomplete },
|
|
63
|
+
{ tester: providerSelectTester, renderer: ProviderSelect },
|
|
40
64
|
];
|
|
41
65
|
|
|
42
66
|
// Export PrimeVue renderers (styles are auto-injected)
|
|
43
67
|
export { primevueRenderers } from "./primevue";
|
|
44
68
|
|
|
45
69
|
// Export individual components
|
|
46
|
-
export { ProviderAutocomplete, ProviderSelect };
|
|
70
|
+
export { ProviderAutocomplete, ProviderSelect, ProviderMultiSelect };
|
|
47
71
|
export { useProvider } from "./composables/useProvider";
|
|
48
72
|
export * from "./testers";
|
|
49
73
|
export * from "./primevue";
|
|
@@ -9,12 +9,48 @@ defineOptions({ name: "JfNumber" });
|
|
|
9
9
|
const props = defineProps(rendererProps<ControlElement>());
|
|
10
10
|
const { control, handleChange } = useJsonFormsControl(props);
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const options = computed(
|
|
13
13
|
() =>
|
|
14
|
-
(control.value.uischema as { options?:
|
|
15
|
-
?.
|
|
14
|
+
(control.value.uischema as { options?: Record<string, unknown> })
|
|
15
|
+
?.options ?? {},
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const placeholder = computed<string | undefined>(
|
|
19
|
+
() => (options.value.placeholder as string) ?? control.value.description,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Currency and decimal configuration
|
|
23
|
+
const mode = computed(() => {
|
|
24
|
+
if (options.value.currency) return "currency";
|
|
25
|
+
if (options.value.decimal || typeof options.value.precision === "number")
|
|
26
|
+
return "decimal";
|
|
27
|
+
return undefined;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const currency = computed(() =>
|
|
31
|
+
typeof options.value.currency === "string" ? options.value.currency : "USD",
|
|
16
32
|
);
|
|
17
33
|
|
|
34
|
+
const minFractionDigits = computed(() => {
|
|
35
|
+
if (mode.value === "currency") return 2;
|
|
36
|
+
if (typeof options.value.precision === "number")
|
|
37
|
+
return options.value.precision;
|
|
38
|
+
return undefined;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const maxFractionDigits = computed(() => {
|
|
42
|
+
if (mode.value === "currency") return 2;
|
|
43
|
+
if (typeof options.value.precision === "number")
|
|
44
|
+
return options.value.precision;
|
|
45
|
+
return undefined;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const useGrouping = computed(() => {
|
|
49
|
+
// Enable grouping for currency by default, or if explicitly set
|
|
50
|
+
if (mode.value === "currency") return true;
|
|
51
|
+
return options.value.useGrouping === true;
|
|
52
|
+
});
|
|
53
|
+
|
|
18
54
|
const onNumber = (val: number | null) =>
|
|
19
55
|
handleChange(control.value.path, val ?? undefined);
|
|
20
56
|
</script>
|
|
@@ -30,7 +66,11 @@ const onNumber = (val: number | null) =>
|
|
|
30
66
|
<InputNumber
|
|
31
67
|
class="w-full"
|
|
32
68
|
input-class="w-full"
|
|
33
|
-
:use-grouping="
|
|
69
|
+
:use-grouping="useGrouping"
|
|
70
|
+
:mode="mode"
|
|
71
|
+
:currency="currency"
|
|
72
|
+
:min-fraction-digits="minFractionDigits"
|
|
73
|
+
:max-fraction-digits="maxFractionDigits"
|
|
34
74
|
:model-value="typeof control.data === 'number' ? control.data : null"
|
|
35
75
|
:placeholder="placeholder"
|
|
36
76
|
:disabled="!control.enabled"
|