@narrative.io/jsonforms-provider-protocols 3.0.0-beta.3 → 3.0.0-beta.5
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/core/projection.d.ts.map +1 -1
- package/dist/core/projection.js.map +1 -1
- package/dist/core/transforms.d.ts.map +1 -1
- package/dist/core/transforms.js +3 -1
- package/dist/core/transforms.js.map +1 -1
- package/dist/jsonforms-provider-protocols.css +2 -2
- package/dist/vue/components/ProviderAutocomplete.vue.d.ts.map +1 -1
- package/dist/vue/components/ProviderAutocomplete.vue.js +8 -5
- package/dist/vue/components/ProviderAutocomplete.vue.js.map +1 -1
- package/dist/vue/components/ProviderMultiSelect.vue.d.ts.map +1 -1
- package/dist/vue/components/ProviderMultiSelect.vue.js +1 -1
- package/dist/vue/components/ProviderMultiSelect.vue2.js +8 -5
- package/dist/vue/components/ProviderMultiSelect.vue2.js.map +1 -1
- package/dist/vue/components/ProviderSelect.vue.d.ts.map +1 -1
- package/dist/vue/components/ProviderSelect.vue.js +1 -1
- package/dist/vue/components/ProviderSelect.vue2.js +10 -5
- package/dist/vue/components/ProviderSelect.vue2.js.map +1 -1
- package/dist/vue/composables/useDerive.d.ts +1 -1
- package/dist/vue/composables/useDerive.d.ts.map +1 -1
- package/dist/vue/composables/useDerive.js +5 -1
- package/dist/vue/composables/useDerive.js.map +1 -1
- package/dist/vue/composables/useDeriveInitialValue.d.ts +36 -0
- package/dist/vue/composables/useDeriveInitialValue.d.ts.map +1 -0
- package/dist/vue/composables/useDeriveInitialValue.js +125 -0
- package/dist/vue/composables/useDeriveInitialValue.js.map +1 -0
- package/dist/vue/composables/useProjection.d.ts.map +1 -1
- package/dist/vue/composables/useProjection.js +1 -3
- package/dist/vue/composables/useProjection.js.map +1 -1
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +12 -2
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/primevue/JfBoolean.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfBoolean.vue.js +14 -3
- package/dist/vue/primevue/JfBoolean.vue.js.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.js +12 -5
- package/dist/vue/primevue/JfEnum.vue.js.map +1 -1
- package/dist/vue/primevue/JfEnumArray.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfEnumArray.vue.js +10 -5
- package/dist/vue/primevue/JfEnumArray.vue.js.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.js +11 -6
- package/dist/vue/primevue/JfNumber.vue.js.map +1 -1
- package/dist/vue/primevue/JfText.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfText.vue.js +13 -6
- package/dist/vue/primevue/JfText.vue.js.map +1 -1
- package/dist/vue/primevue/JfTextArea.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfTextArea.vue.js +10 -5
- package/dist/vue/primevue/JfTextArea.vue.js.map +1 -1
- package/dist/vue/primevue/index.d.ts.map +1 -1
- package/dist/vue/primevue/index.js +93 -16
- package/dist/vue/primevue/index.js.map +1 -1
- package/dist/vue/utils/autoSelect.js.map +1 -1
- package/package.json +1 -1
- package/src/core/projection.ts +5 -5
- package/src/core/transforms.ts +33 -6
- package/src/vue/components/ProviderAutocomplete.vue +8 -5
- package/src/vue/components/ProviderMultiSelect.vue +12 -7
- package/src/vue/components/ProviderSelect.vue +13 -6
- package/src/vue/composables/useDerive.ts +16 -3
- package/src/vue/composables/useDeriveInitialValue.ts +195 -0
- package/src/vue/composables/useProjection.ts +6 -12
- package/src/vue/index.ts +12 -4
- package/src/vue/primevue/JfBoolean.vue +8 -3
- package/src/vue/primevue/JfEnum.vue +15 -8
- package/src/vue/primevue/JfEnumArray.vue +15 -12
- package/src/vue/primevue/JfNumber.vue +11 -8
- package/src/vue/primevue/JfText.vue +15 -8
- package/src/vue/primevue/JfTextArea.vue +10 -7
- package/src/vue/primevue/index.ts +104 -23
- package/src/vue/styles.css +26 -1
- package/src/vue/utils/autoSelect.ts +2 -2
|
@@ -3,6 +3,7 @@ import type { ControlElement, JsonSchema } from "@jsonforms/core";
|
|
|
3
3
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
4
4
|
import { computed, inject, watch } from "vue";
|
|
5
5
|
import { useProvider } from "../composables/useProvider";
|
|
6
|
+
import { useDeriveInitialValue } from "../composables/useDeriveInitialValue";
|
|
6
7
|
import { useProjection } from "../composables/useProjection";
|
|
7
8
|
import { shouldAutoSelect } from "../utils/autoSelect";
|
|
8
9
|
import Dropdown from "primevue/dropdown";
|
|
@@ -13,7 +14,10 @@ const props = defineProps<{
|
|
|
13
14
|
path: string;
|
|
14
15
|
}>();
|
|
15
16
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
16
|
-
const { projectedData, handleProjectedChange: handleChange } = useProjection(
|
|
17
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(
|
|
18
|
+
control,
|
|
19
|
+
rawHandleChange,
|
|
20
|
+
);
|
|
17
21
|
|
|
18
22
|
const binding = computed(() => {
|
|
19
23
|
const provider = control.value.uischema?.options?.provider;
|
|
@@ -46,6 +50,9 @@ const { items, loading, error } = useProvider(binding, {
|
|
|
46
50
|
|
|
47
51
|
// Provider will automatically reload when rootData changes due to reactive cache key
|
|
48
52
|
|
|
53
|
+
// deriveInitialValue — async API-based initial value seeding
|
|
54
|
+
useDeriveInitialValue({ control, handleChange });
|
|
55
|
+
|
|
49
56
|
// Auto-select when provider returns only one item (enabled by default)
|
|
50
57
|
watch(
|
|
51
58
|
[items, loading],
|
|
@@ -62,7 +69,7 @@ watch(
|
|
|
62
69
|
handleChange(control.value.path, valueToSelect);
|
|
63
70
|
}
|
|
64
71
|
},
|
|
65
|
-
{ immediate: true }
|
|
72
|
+
{ immediate: true },
|
|
66
73
|
);
|
|
67
74
|
|
|
68
75
|
const value = computed({
|
|
@@ -81,16 +88,16 @@ const placeholder = computed(() => {
|
|
|
81
88
|
</script>
|
|
82
89
|
|
|
83
90
|
<template>
|
|
84
|
-
<div class="
|
|
85
|
-
<label v-if="control.schema.title" class="
|
|
91
|
+
<div class="jf-control">
|
|
92
|
+
<label v-if="control.schema.title" class="jf-label">{{
|
|
86
93
|
control.schema.title
|
|
87
94
|
}}</label>
|
|
88
|
-
<div v-if="control.description" class="
|
|
95
|
+
<div v-if="control.description" class="jf-description">
|
|
89
96
|
{{ control.description }}
|
|
90
97
|
</div>
|
|
91
98
|
<Dropdown
|
|
92
99
|
v-model="value"
|
|
93
|
-
class="w-full"
|
|
100
|
+
class="w-full!"
|
|
94
101
|
:options="items"
|
|
95
102
|
option-label="label"
|
|
96
103
|
option-value="value"
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
watch,
|
|
4
|
+
unref,
|
|
5
|
+
inject,
|
|
6
|
+
type Ref,
|
|
7
|
+
type ComputedRef,
|
|
8
|
+
} from "vue";
|
|
2
9
|
import { type ControlElement } from "@jsonforms/core";
|
|
3
10
|
import { useDataLayer } from "./useDataLayer";
|
|
4
11
|
|
|
@@ -14,7 +21,11 @@ interface DeriveOptions {
|
|
|
14
21
|
data?: Ref<unknown> | ComputedRef<unknown>;
|
|
15
22
|
}
|
|
16
23
|
|
|
17
|
-
export function useDerive({
|
|
24
|
+
export function useDerive({
|
|
25
|
+
control,
|
|
26
|
+
handleChange,
|
|
27
|
+
data: dataOverride,
|
|
28
|
+
}: DeriveOptions) {
|
|
18
29
|
// Get the root form data from JSONForms context
|
|
19
30
|
const injectedFormData = inject<{ value: unknown }>("formData", {
|
|
20
31
|
value: {},
|
|
@@ -50,7 +61,9 @@ export function useDerive({ control, handleChange, data: dataOverride }: DeriveO
|
|
|
50
61
|
data,
|
|
51
62
|
extData,
|
|
52
63
|
);
|
|
53
|
-
const compareData = dataOverride
|
|
64
|
+
const compareData = dataOverride
|
|
65
|
+
? unref(dataOverride)
|
|
66
|
+
: control.value.data;
|
|
54
67
|
if (derivedValue !== compareData) {
|
|
55
68
|
handleChange(control.value.path, derivedValue);
|
|
56
69
|
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { computed, inject, ref, watch, type Ref, type ComputedRef } from "vue";
|
|
2
|
+
import { type ControlElement } from "@jsonforms/core";
|
|
3
|
+
import { renderTpl, renderObj } from "../../core/templating";
|
|
4
|
+
import { jp } from "../../core/jsonpath";
|
|
5
|
+
import {
|
|
6
|
+
applyTransformPipeline,
|
|
7
|
+
type TransformPipeline,
|
|
8
|
+
} from "../../core/transforms";
|
|
9
|
+
import type { AuthConfig } from "../../core/types";
|
|
10
|
+
|
|
11
|
+
export interface DeriveInitialValueCfg {
|
|
12
|
+
protocol: string;
|
|
13
|
+
config: {
|
|
14
|
+
url: string;
|
|
15
|
+
method?: "GET" | "POST";
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
query?: Record<string, unknown>;
|
|
18
|
+
body?: unknown;
|
|
19
|
+
auth?: AuthConfig;
|
|
20
|
+
items: string;
|
|
21
|
+
map: { value: string };
|
|
22
|
+
transforms?: TransformPipeline;
|
|
23
|
+
showError?: boolean;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface DeriveInitialValueOptions {
|
|
28
|
+
control: Ref<{
|
|
29
|
+
uischema: ControlElement;
|
|
30
|
+
path: string;
|
|
31
|
+
data: unknown;
|
|
32
|
+
}>;
|
|
33
|
+
handleChange: (path: string, value: unknown) => void;
|
|
34
|
+
data?: Ref<unknown> | ComputedRef<unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function buildAuthHeaders(
|
|
38
|
+
auth?: AuthConfig,
|
|
39
|
+
globalAuth?: Record<string, unknown>,
|
|
40
|
+
): Record<string, string> {
|
|
41
|
+
const headers: Record<string, string> = {};
|
|
42
|
+
if (!auth) return headers;
|
|
43
|
+
|
|
44
|
+
if (auth.use && globalAuth?.[auth.use]) {
|
|
45
|
+
const globalValue = globalAuth[auth.use];
|
|
46
|
+
const value =
|
|
47
|
+
typeof globalValue === "function" ? globalValue() : globalValue;
|
|
48
|
+
if (auth.use === "apiKey") headers["X-API-Key"] = String(value);
|
|
49
|
+
else if (auth.use === "bearer")
|
|
50
|
+
headers["Authorization"] = `Bearer ${value}`;
|
|
51
|
+
else if (auth.use === "token") headers["Authorization"] = `Token ${value}`;
|
|
52
|
+
return headers;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (auth.bearer) {
|
|
56
|
+
const v = typeof auth.bearer === "function" ? auth.bearer() : auth.bearer;
|
|
57
|
+
headers["Authorization"] = `Bearer ${v}`;
|
|
58
|
+
}
|
|
59
|
+
if (auth.apiKey) {
|
|
60
|
+
const v = typeof auth.apiKey === "function" ? auth.apiKey() : auth.apiKey;
|
|
61
|
+
headers["X-API-Key"] = String(v);
|
|
62
|
+
}
|
|
63
|
+
if (auth.token) {
|
|
64
|
+
const v = typeof auth.token === "function" ? auth.token() : auth.token;
|
|
65
|
+
headers["Authorization"] = `Token ${v}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return headers;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns true when the template URL contains `{{…}}` placeholders but
|
|
73
|
+
* one or more of those placeholders resolved to an empty string, which
|
|
74
|
+
* means a required data dependency hasn't been set yet.
|
|
75
|
+
*/
|
|
76
|
+
function hasUnresolvedTemplates(
|
|
77
|
+
templateUrl: string,
|
|
78
|
+
renderedUrl: string,
|
|
79
|
+
): boolean {
|
|
80
|
+
if (!templateUrl.includes("{{")) return false;
|
|
81
|
+
// After protocol, empty segments indicate unresolved vars
|
|
82
|
+
const pathPart = renderedUrl.replace(/^https?:\/\/[^/]+/, "");
|
|
83
|
+
if (pathPart.includes("//")) return true;
|
|
84
|
+
// Trailing slash when the template didn't have one
|
|
85
|
+
if (renderedUrl.endsWith("/") && !templateUrl.endsWith("/")) return true;
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function useDeriveInitialValue({
|
|
90
|
+
control,
|
|
91
|
+
handleChange,
|
|
92
|
+
}: DeriveInitialValueOptions) {
|
|
93
|
+
const injectedFormData = inject<{ value: unknown }>("formData", {
|
|
94
|
+
value: {},
|
|
95
|
+
});
|
|
96
|
+
const rootData = computed(() => injectedFormData.value || {});
|
|
97
|
+
const auth = inject("providerAuth", {}) as Record<string, unknown>;
|
|
98
|
+
|
|
99
|
+
const cfg = computed<DeriveInitialValueCfg | undefined>(() => {
|
|
100
|
+
return control.value.uischema?.options?.deriveInitialValue as
|
|
101
|
+
| DeriveInitialValueCfg
|
|
102
|
+
| undefined;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Compute the resolved URL reactively
|
|
106
|
+
const resolvedUrl = computed<string | null>(() => {
|
|
107
|
+
const c = cfg.value;
|
|
108
|
+
if (!c?.config?.url) return null;
|
|
109
|
+
const rendered = renderTpl(c.config.url, { data: rootData.value });
|
|
110
|
+
if (hasUnresolvedTemplates(c.config.url, rendered)) return null;
|
|
111
|
+
return rendered;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const lastFetchedUrl = ref<string | null>(null);
|
|
115
|
+
const loading = ref(false);
|
|
116
|
+
const error = ref<string | undefined>(undefined);
|
|
117
|
+
|
|
118
|
+
watch(
|
|
119
|
+
resolvedUrl,
|
|
120
|
+
async (url) => {
|
|
121
|
+
if (!url || !cfg.value) return;
|
|
122
|
+
// Only fetch when the URL changes (new context).
|
|
123
|
+
// Same URL = same context; don't override user selection.
|
|
124
|
+
if (url === lastFetchedUrl.value) return;
|
|
125
|
+
lastFetchedUrl.value = url;
|
|
126
|
+
|
|
127
|
+
loading.value = true;
|
|
128
|
+
error.value = undefined;
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const c = cfg.value.config;
|
|
132
|
+
const fullUrl = new URL(url);
|
|
133
|
+
|
|
134
|
+
// Query params
|
|
135
|
+
const q = renderObj(c.query ?? {}, {
|
|
136
|
+
data: rootData.value,
|
|
137
|
+
}) as Record<string, unknown>;
|
|
138
|
+
for (const [k, v] of Object.entries(q)) {
|
|
139
|
+
if (v !== undefined && v !== "")
|
|
140
|
+
fullUrl.searchParams.set(k, String(v));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Headers
|
|
144
|
+
const baseHeaders = renderObj(c.headers ?? {}, {
|
|
145
|
+
data: rootData.value,
|
|
146
|
+
}) as Record<string, string>;
|
|
147
|
+
const authHeaders = buildAuthHeaders(c.auth, auth);
|
|
148
|
+
const headers = { ...baseHeaders, ...authHeaders };
|
|
149
|
+
|
|
150
|
+
const method = c.method ?? "GET";
|
|
151
|
+
const requestInit: RequestInit = { method, headers };
|
|
152
|
+
if (method !== "GET" && c.body) {
|
|
153
|
+
requestInit.body = JSON.stringify(
|
|
154
|
+
renderObj(c.body, { data: rootData.value }),
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const res = await fetch(fullUrl.toString(), requestInit);
|
|
159
|
+
if (!res.ok) {
|
|
160
|
+
if (c.showError !== false) {
|
|
161
|
+
throw new Error(`REST ${res.status}`);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const json = await res.json();
|
|
167
|
+
let items = jp(json, c.items);
|
|
168
|
+
|
|
169
|
+
// Apply transforms if provided
|
|
170
|
+
if (c.transforms && c.transforms.length > 0) {
|
|
171
|
+
items = applyTransformPipeline(items, c.transforms);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (items.length === 0) return; // No items → leave field empty
|
|
175
|
+
|
|
176
|
+
// Extract value from first item
|
|
177
|
+
const derivedValue = jp(items[0], c.map.value)[0];
|
|
178
|
+
if (derivedValue !== undefined) {
|
|
179
|
+
handleChange(control.value.path, derivedValue);
|
|
180
|
+
}
|
|
181
|
+
} catch (e) {
|
|
182
|
+
error.value = (e as Error)?.message ?? String(e);
|
|
183
|
+
console.warn(
|
|
184
|
+
`deriveInitialValue fetch failed for ${control.value.path}:`,
|
|
185
|
+
e,
|
|
186
|
+
);
|
|
187
|
+
} finally {
|
|
188
|
+
loading.value = false;
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
{ immediate: true },
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return { loading, error };
|
|
195
|
+
}
|
|
@@ -27,15 +27,9 @@ interface ErrorLike {
|
|
|
27
27
|
* Resolve the display label for a control.
|
|
28
28
|
* Priority: uischema options.label → schemaTitle (projected sub-schema) → control.label.
|
|
29
29
|
*/
|
|
30
|
-
function resolveLabel(
|
|
31
|
-
ctrl: ProjectionControl,
|
|
32
|
-
schemaTitle?: string,
|
|
33
|
-
): string {
|
|
30
|
+
function resolveLabel(ctrl: ProjectionControl, schemaTitle?: string): string {
|
|
34
31
|
return (
|
|
35
|
-
(ctrl.uischema?.options?.label as string) ??
|
|
36
|
-
schemaTitle ??
|
|
37
|
-
ctrl.label ??
|
|
38
|
-
""
|
|
32
|
+
(ctrl.uischema?.options?.label as string) ?? schemaTitle ?? ctrl.label ?? ""
|
|
39
33
|
);
|
|
40
34
|
}
|
|
41
35
|
|
|
@@ -69,7 +63,9 @@ function prefixErrors(label: string, errors: string): string {
|
|
|
69
63
|
function getErrorPath(error: ErrorLike): string {
|
|
70
64
|
let p = (error.instancePath || "").replace(/\//g, ".").replace(/^\./, "");
|
|
71
65
|
if (error.keyword === "required" && error.params?.missingProperty) {
|
|
72
|
-
p = p
|
|
66
|
+
p = p
|
|
67
|
+
? p + "." + error.params.missingProperty
|
|
68
|
+
: error.params.missingProperty;
|
|
73
69
|
}
|
|
74
70
|
return p;
|
|
75
71
|
}
|
|
@@ -160,9 +156,7 @@ export function useProjection(
|
|
|
160
156
|
errStr = baseErrors;
|
|
161
157
|
} else {
|
|
162
158
|
const projMsg = matching
|
|
163
|
-
.map((e) =>
|
|
164
|
-
e.keyword === "required" ? "is required" : e.message,
|
|
165
|
-
)
|
|
159
|
+
.map((e) => (e.keyword === "required" ? "is required" : e.message))
|
|
166
160
|
.filter(Boolean)
|
|
167
161
|
.join("\n");
|
|
168
162
|
errStr = [baseErrors, projMsg].filter(Boolean).join("\n");
|
package/src/vue/index.ts
CHANGED
|
@@ -22,8 +22,11 @@ const isIntegerScope = (uischema: unknown, schema: unknown) => {
|
|
|
22
22
|
const ui = uischema as { type?: string; scope?: string };
|
|
23
23
|
if (ui?.type !== "Control" || !ui?.scope) return false;
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const propertySchema = resolveScopeSchema(
|
|
26
|
+
ui.scope,
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
schema as Record<string, any>,
|
|
29
|
+
);
|
|
27
30
|
return propertySchema?.type === "integer";
|
|
28
31
|
};
|
|
29
32
|
|
|
@@ -53,8 +56,11 @@ const isArrayControl = (uischema: UISchemaElement, schema: unknown) => {
|
|
|
53
56
|
return false;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
const propertySchema = resolveScopeSchema(
|
|
60
|
+
controlSchema.scope,
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
62
|
+
schema as Record<string, any>,
|
|
63
|
+
);
|
|
58
64
|
return propertySchema?.type === "array";
|
|
59
65
|
};
|
|
60
66
|
|
|
@@ -79,6 +85,8 @@ export { useProjection } from "./composables/useProjection";
|
|
|
79
85
|
export type { ProjectionResult } from "./composables/useProjection";
|
|
80
86
|
export { createDataLayer, useDataLayer } from "./composables/useDataLayer";
|
|
81
87
|
export type { DataLayer } from "./composables/useDataLayer";
|
|
88
|
+
export { useDeriveInitialValue } from "./composables/useDeriveInitialValue";
|
|
89
|
+
export type { DeriveInitialValueCfg } from "./composables/useDeriveInitialValue";
|
|
82
90
|
export { useDirtyValidation } from "./composables/useDirtyValidation";
|
|
83
91
|
export * from "./testers";
|
|
84
92
|
|
|
@@ -49,7 +49,12 @@ import Checkbox from "primevue/checkbox";
|
|
|
49
49
|
const instance = getCurrentInstance()!;
|
|
50
50
|
const props = instance.props as unknown as ControlProps;
|
|
51
51
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
52
|
-
const {
|
|
52
|
+
const {
|
|
53
|
+
projectedData,
|
|
54
|
+
handleProjectedChange: handleChange,
|
|
55
|
+
projectedErrors,
|
|
56
|
+
projectedLabel,
|
|
57
|
+
} = useProjection(control, rawHandleChange);
|
|
53
58
|
|
|
54
59
|
// Track user interaction — errors only show after first toggle
|
|
55
60
|
const { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);
|
|
@@ -61,7 +66,7 @@ const onToggle = (val: boolean) => {
|
|
|
61
66
|
</script>
|
|
62
67
|
|
|
63
68
|
<template>
|
|
64
|
-
<div class="flex items
|
|
69
|
+
<div class="jf-control" style="flex-direction: row; align-items: center">
|
|
65
70
|
<Checkbox
|
|
66
71
|
:binary="true"
|
|
67
72
|
:model-value="!!projectedData"
|
|
@@ -70,7 +75,7 @@ const onToggle = (val: boolean) => {
|
|
|
70
75
|
:aria-invalid="showErrors || undefined"
|
|
71
76
|
@update:model-value="onToggle"
|
|
72
77
|
/>
|
|
73
|
-
<label v-if="projectedLabel">{{ projectedLabel }}</label>
|
|
78
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
74
79
|
<small v-if="showErrors" class="p-error">{{ projectedErrors }}</small>
|
|
75
80
|
</div>
|
|
76
81
|
</template>
|
|
@@ -45,6 +45,7 @@ import { useJsonFormsControl } from "@jsonforms/vue";
|
|
|
45
45
|
import { computed, inject, getCurrentInstance, watch } from "vue";
|
|
46
46
|
import { useProvider } from "../composables/useProvider";
|
|
47
47
|
import { useDerive } from "../composables/useDerive";
|
|
48
|
+
import { useDeriveInitialValue } from "../composables/useDeriveInitialValue";
|
|
48
49
|
import { useProjection } from "../composables/useProjection";
|
|
49
50
|
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
50
51
|
import { shouldAutoSelect } from "../utils/autoSelect";
|
|
@@ -54,7 +55,12 @@ import Dropdown from "primevue/dropdown";
|
|
|
54
55
|
const instance = getCurrentInstance()!;
|
|
55
56
|
const props = instance.props as unknown as ControlProps;
|
|
56
57
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
57
|
-
const {
|
|
58
|
+
const {
|
|
59
|
+
projectedData,
|
|
60
|
+
handleProjectedChange: handleChange,
|
|
61
|
+
projectedErrors,
|
|
62
|
+
projectedLabel,
|
|
63
|
+
} = useProjection(control, rawHandleChange);
|
|
58
64
|
|
|
59
65
|
type Opt = { label: string; value: unknown };
|
|
60
66
|
const toOptions = (schema?: JsonSchema): Opt[] => {
|
|
@@ -142,6 +148,9 @@ const options = computed(() => {
|
|
|
142
148
|
// Add derive functionality
|
|
143
149
|
useDerive({ control, handleChange, data: projectedData });
|
|
144
150
|
|
|
151
|
+
// Add deriveInitialValue — async API-based initial value seeding
|
|
152
|
+
useDeriveInitialValue({ control, handleChange });
|
|
153
|
+
|
|
145
154
|
// Auto-select when provider returns only one item (enabled by default)
|
|
146
155
|
watch(
|
|
147
156
|
[providerItems, loading],
|
|
@@ -158,7 +167,7 @@ watch(
|
|
|
158
167
|
handleChange(control.value.path, valueToSelect);
|
|
159
168
|
}
|
|
160
169
|
},
|
|
161
|
-
{ immediate: true }
|
|
170
|
+
{ immediate: true },
|
|
162
171
|
);
|
|
163
172
|
|
|
164
173
|
// Track user interaction — errors only show after blur
|
|
@@ -170,15 +179,13 @@ const onSelect = (val: unknown) => {
|
|
|
170
179
|
</script>
|
|
171
180
|
|
|
172
181
|
<template>
|
|
173
|
-
<div class="
|
|
174
|
-
<label v-if="projectedLabel" class="
|
|
175
|
-
|
|
176
|
-
}}</label>
|
|
177
|
-
<div v-if="control.description" class="text-color-secondary text-left">
|
|
182
|
+
<div class="jf-control">
|
|
183
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
184
|
+
<div v-if="control.description" class="jf-description">
|
|
178
185
|
{{ control.description }}
|
|
179
186
|
</div>
|
|
180
187
|
<Dropdown
|
|
181
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
188
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
182
189
|
:options="options"
|
|
183
190
|
option-label="label"
|
|
184
191
|
option-value="value"
|
|
@@ -53,7 +53,12 @@ import MultiSelect from "primevue/multiselect";
|
|
|
53
53
|
const instance = getCurrentInstance()!;
|
|
54
54
|
const props = instance.props as unknown as ControlProps;
|
|
55
55
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
56
|
-
const {
|
|
56
|
+
const {
|
|
57
|
+
projectedData,
|
|
58
|
+
handleProjectedChange: handleChange,
|
|
59
|
+
projectedErrors,
|
|
60
|
+
projectedLabel,
|
|
61
|
+
} = useProjection(control, rawHandleChange);
|
|
57
62
|
|
|
58
63
|
type Opt = { label: string; value: unknown };
|
|
59
64
|
const toOptions = (schema?: JsonSchema): Opt[] => {
|
|
@@ -145,14 +150,16 @@ watch(
|
|
|
145
150
|
control.value.uischema?.options?.autoSelectSingle === true,
|
|
146
151
|
isLoading,
|
|
147
152
|
items,
|
|
148
|
-
currentValue: Array.isArray(projectedData.value)
|
|
153
|
+
currentValue: Array.isArray(projectedData.value)
|
|
154
|
+
? projectedData.value
|
|
155
|
+
: [],
|
|
149
156
|
});
|
|
150
157
|
|
|
151
158
|
if (valueToSelect !== null) {
|
|
152
159
|
handleChange(control.value.path, valueToSelect);
|
|
153
160
|
}
|
|
154
161
|
},
|
|
155
|
-
{ immediate: true }
|
|
162
|
+
{ immediate: true },
|
|
156
163
|
);
|
|
157
164
|
|
|
158
165
|
const placeholder = computed<string | undefined>(() => {
|
|
@@ -190,17 +197,15 @@ const model = computed<unknown[]>({
|
|
|
190
197
|
</script>
|
|
191
198
|
|
|
192
199
|
<template>
|
|
193
|
-
<div class="
|
|
194
|
-
<label v-if="projectedLabel" class="
|
|
195
|
-
|
|
196
|
-
}}</label>
|
|
197
|
-
<div v-if="control.description" class="text-color-secondary text-left">
|
|
200
|
+
<div class="jf-control">
|
|
201
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
202
|
+
<div v-if="control.description" class="jf-description">
|
|
198
203
|
{{ control.description }}
|
|
199
204
|
</div>
|
|
200
205
|
|
|
201
206
|
<MultiSelect
|
|
202
207
|
v-model="model"
|
|
203
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
208
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
204
209
|
:options="options"
|
|
205
210
|
option-label="label"
|
|
206
211
|
option-value="value"
|
|
@@ -214,8 +219,6 @@ const model = computed<unknown[]>({
|
|
|
214
219
|
<small v-if="error" class="p-error" role="alert"
|
|
215
220
|
>Failed to load: {{ error }}</small
|
|
216
221
|
>
|
|
217
|
-
<small v-else-if="showErrors" class="p-error">{{
|
|
218
|
-
projectedErrors
|
|
219
|
-
}}</small>
|
|
222
|
+
<small v-else-if="showErrors" class="p-error">{{ projectedErrors }}</small>
|
|
220
223
|
</div>
|
|
221
224
|
</template>
|
|
@@ -50,7 +50,12 @@ import InputNumber from "primevue/inputnumber";
|
|
|
50
50
|
const instance = getCurrentInstance()!;
|
|
51
51
|
const props = instance.props as unknown as ControlProps;
|
|
52
52
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
53
|
-
const {
|
|
53
|
+
const {
|
|
54
|
+
projectedData,
|
|
55
|
+
handleProjectedChange: handleChange,
|
|
56
|
+
projectedErrors,
|
|
57
|
+
projectedLabel,
|
|
58
|
+
} = useProjection(control, rawHandleChange);
|
|
54
59
|
|
|
55
60
|
const options = computed(
|
|
56
61
|
() =>
|
|
@@ -110,16 +115,14 @@ const onNumber = (val: number | null) => {
|
|
|
110
115
|
</script>
|
|
111
116
|
|
|
112
117
|
<template>
|
|
113
|
-
<div class="
|
|
114
|
-
<label v-if="projectedLabel" class="
|
|
115
|
-
|
|
116
|
-
}}</label>
|
|
117
|
-
<div v-if="control.description" class="text-color-secondary text-left">
|
|
118
|
+
<div class="jf-control">
|
|
119
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
120
|
+
<div v-if="control.description" class="jf-description">
|
|
118
121
|
{{ control.description }}
|
|
119
122
|
</div>
|
|
120
123
|
<InputNumber
|
|
121
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
122
|
-
:input-class="['w-full', { 'p-invalid': showErrors }]"
|
|
124
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
125
|
+
:input-class="['w-full!', { 'p-invalid': showErrors }]"
|
|
123
126
|
:use-grouping="useGrouping"
|
|
124
127
|
:mode="mode"
|
|
125
128
|
:currency="currency"
|
|
@@ -43,6 +43,7 @@ import { useJsonFormsControl } from "@jsonforms/vue";
|
|
|
43
43
|
import { computed, ref, inject, watch, getCurrentInstance } from "vue";
|
|
44
44
|
import { useProvider } from "../composables/useProvider";
|
|
45
45
|
import { useDerive } from "../composables/useDerive";
|
|
46
|
+
import { useDeriveInitialValue } from "../composables/useDeriveInitialValue";
|
|
46
47
|
import { useProjection } from "../composables/useProjection";
|
|
47
48
|
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
48
49
|
import InputText from "primevue/inputtext";
|
|
@@ -52,7 +53,12 @@ import AutoComplete from "primevue/autocomplete";
|
|
|
52
53
|
const instance = getCurrentInstance()!;
|
|
53
54
|
const props = instance.props as unknown as ControlProps;
|
|
54
55
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
55
|
-
const {
|
|
56
|
+
const {
|
|
57
|
+
projectedData,
|
|
58
|
+
handleProjectedChange: handleChange,
|
|
59
|
+
projectedErrors,
|
|
60
|
+
projectedLabel,
|
|
61
|
+
} = useProjection(control, rawHandleChange);
|
|
56
62
|
|
|
57
63
|
// Provider support for autocomplete functionality
|
|
58
64
|
const binding = computed(() => {
|
|
@@ -118,6 +124,9 @@ const isAutocomplete = computed(() => !!binding.value);
|
|
|
118
124
|
// Add derive functionality
|
|
119
125
|
useDerive({ control, handleChange, data: projectedData });
|
|
120
126
|
|
|
127
|
+
// Add deriveInitialValue — async API-based initial value seeding
|
|
128
|
+
useDeriveInitialValue({ control, handleChange });
|
|
129
|
+
|
|
121
130
|
// Track user interaction — errors only show after blur
|
|
122
131
|
const { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);
|
|
123
132
|
|
|
@@ -150,16 +159,14 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
150
159
|
</script>
|
|
151
160
|
|
|
152
161
|
<template>
|
|
153
|
-
<div class="
|
|
154
|
-
<label v-if="projectedLabel" class="
|
|
155
|
-
|
|
156
|
-
}}</label>
|
|
157
|
-
<div v-if="control.description" class="text-color-secondary text-left">
|
|
162
|
+
<div class="jf-control">
|
|
163
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
164
|
+
<div v-if="control.description" class="jf-description">
|
|
158
165
|
{{ control.description }}
|
|
159
166
|
</div>
|
|
160
167
|
<AutoComplete
|
|
161
168
|
v-if="isAutocomplete"
|
|
162
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
169
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
163
170
|
:model-value="projectedData ?? ''"
|
|
164
171
|
:suggestions="items"
|
|
165
172
|
option-label="label"
|
|
@@ -173,7 +180,7 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
173
180
|
/>
|
|
174
181
|
<InputText
|
|
175
182
|
v-else
|
|
176
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
183
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
177
184
|
:model-value="(projectedData as string) ?? ''"
|
|
178
185
|
:disabled="!control.enabled"
|
|
179
186
|
:aria-invalid="showErrors || undefined"
|
|
@@ -49,7 +49,12 @@ import Textarea from "primevue/textarea";
|
|
|
49
49
|
const instance = getCurrentInstance()!;
|
|
50
50
|
const props = instance.props as unknown as ControlProps;
|
|
51
51
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
52
|
-
const {
|
|
52
|
+
const {
|
|
53
|
+
projectedData,
|
|
54
|
+
handleProjectedChange: handleChange,
|
|
55
|
+
projectedErrors,
|
|
56
|
+
projectedLabel,
|
|
57
|
+
} = useProjection(control, rawHandleChange);
|
|
53
58
|
|
|
54
59
|
const placeholder = computed<string | undefined>(
|
|
55
60
|
() =>
|
|
@@ -79,15 +84,13 @@ function onBlur() {
|
|
|
79
84
|
</script>
|
|
80
85
|
|
|
81
86
|
<template>
|
|
82
|
-
<div class="
|
|
83
|
-
<label v-if="projectedLabel" class="
|
|
84
|
-
|
|
85
|
-
}}</label>
|
|
86
|
-
<div v-if="control.description" class="text-color-secondary text-left">
|
|
87
|
+
<div class="jf-control">
|
|
88
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
89
|
+
<div v-if="control.description" class="jf-description">
|
|
87
90
|
{{ control.description }}
|
|
88
91
|
</div>
|
|
89
92
|
<Textarea
|
|
90
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
93
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
91
94
|
:model-value="(projectedData as string) ?? ''"
|
|
92
95
|
:disabled="!control.enabled"
|
|
93
96
|
:aria-invalid="showErrors || undefined"
|