@narrative.io/jsonforms-provider-protocols 3.0.0-beta.2 → 3.0.0-beta.4
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 +8 -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/useDirtyValidation.d.ts +3 -3
- package/dist/vue/composables/useDirtyValidation.d.ts.map +1 -1
- package/dist/vue/composables/useDirtyValidation.js +2 -2
- package/dist/vue/composables/useDirtyValidation.js.map +1 -1
- package/dist/vue/composables/useProjection.d.ts +6 -0
- package/dist/vue/composables/useProjection.d.ts.map +1 -1
- package/dist/vue/composables/useProjection.js +54 -3
- package/dist/vue/composables/useProjection.js.map +1 -1
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +10 -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 +17 -6
- 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 +13 -8
- 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 +13 -8
- 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 +14 -9
- 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 +14 -9
- 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 +13 -8
- 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 +3 -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 +9 -6
- package/src/vue/composables/useDerive.ts +16 -3
- package/src/vue/composables/useDirtyValidation.ts +8 -3
- package/src/vue/composables/useProjection.ts +108 -1
- package/src/vue/index.ts +10 -4
- package/src/vue/primevue/JfBoolean.vue +10 -5
- package/src/vue/primevue/JfEnum.vue +13 -10
- package/src/vue/primevue/JfEnumArray.vue +16 -13
- package/src/vue/primevue/JfNumber.vue +13 -10
- package/src/vue/primevue/JfText.vue +13 -10
- package/src/vue/primevue/JfTextArea.vue +12 -9
- package/src/vue/primevue/index.ts +104 -23
- package/src/vue/styles.css +26 -1
- package/src/vue/utils/autoSelect.ts +2 -2
|
@@ -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
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { ref, computed, type Ref } from "vue";
|
|
1
|
+
import { ref, computed, type Ref, type ComputedRef } from "vue";
|
|
2
2
|
|
|
3
|
-
export function useDirtyValidation(
|
|
3
|
+
export function useDirtyValidation(
|
|
4
|
+
control: Ref<{ errors: string }>,
|
|
5
|
+
errorsOverride?: Ref<string> | ComputedRef<string>,
|
|
6
|
+
) {
|
|
4
7
|
const hasInteracted = ref(false);
|
|
5
8
|
|
|
6
9
|
const showErrors = computed(
|
|
7
|
-
() =>
|
|
10
|
+
() =>
|
|
11
|
+
hasInteracted.value &&
|
|
12
|
+
!!(errorsOverride ? errorsOverride.value : control.value.errors),
|
|
8
13
|
);
|
|
9
14
|
|
|
10
15
|
const markDirty = () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, type ComputedRef, type Ref } from "vue";
|
|
1
|
+
import { computed, inject, type ComputedRef, type Ref } from "vue";
|
|
2
2
|
import {
|
|
3
3
|
getProjectedValue,
|
|
4
4
|
setProjectedValue,
|
|
@@ -8,11 +8,68 @@ import {
|
|
|
8
8
|
interface ProjectionControl {
|
|
9
9
|
data: unknown;
|
|
10
10
|
path: string;
|
|
11
|
+
errors: string;
|
|
12
|
+
label?: string;
|
|
11
13
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
14
|
schema: Record<string, any>;
|
|
13
15
|
uischema: { options?: { projection?: string; [key: string]: unknown } };
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
// Minimal AJV ErrorObject shape for filtering
|
|
19
|
+
interface ErrorLike {
|
|
20
|
+
instancePath?: string;
|
|
21
|
+
keyword?: string;
|
|
22
|
+
message?: string;
|
|
23
|
+
params?: { missingProperty?: string };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the display label for a control.
|
|
28
|
+
* Priority: uischema options.label → schemaTitle (projected sub-schema) → control.label.
|
|
29
|
+
*/
|
|
30
|
+
function resolveLabel(ctrl: ProjectionControl, schemaTitle?: string): string {
|
|
31
|
+
return (
|
|
32
|
+
(ctrl.uischema?.options?.label as string) ?? schemaTitle ?? ctrl.label ?? ""
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Normalize AJV error message fragments into user-friendly text.
|
|
38
|
+
* e.g. "is a required property" → "is required"
|
|
39
|
+
*/
|
|
40
|
+
function normalizeErrors(errors: string): string {
|
|
41
|
+
if (!errors) return errors;
|
|
42
|
+
return errors
|
|
43
|
+
.replace(/is a required property/g, "is required")
|
|
44
|
+
.replace(/must have required property '[^']*'/g, "is required");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Prefix each error message line with the field label so that AJV fragments
|
|
49
|
+
* like "is required" become "Name is required".
|
|
50
|
+
*/
|
|
51
|
+
function prefixErrors(label: string, errors: string): string {
|
|
52
|
+
if (!label || !errors) return errors;
|
|
53
|
+
return errors
|
|
54
|
+
.split("\n")
|
|
55
|
+
.map((line) => `${label} ${line}`)
|
|
56
|
+
.join("\n");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Convert an AJV ErrorObject's instancePath to a dot-separated control path.
|
|
61
|
+
* Replicates the logic from @jsonforms/core getControlPath.
|
|
62
|
+
*/
|
|
63
|
+
function getErrorPath(error: ErrorLike): string {
|
|
64
|
+
let p = (error.instancePath || "").replace(/\//g, ".").replace(/^\./, "");
|
|
65
|
+
if (error.keyword === "required" && error.params?.missingProperty) {
|
|
66
|
+
p = p
|
|
67
|
+
? p + "." + error.params.missingProperty
|
|
68
|
+
: error.params.missingProperty;
|
|
69
|
+
}
|
|
70
|
+
return p;
|
|
71
|
+
}
|
|
72
|
+
|
|
16
73
|
export interface ProjectionResult {
|
|
17
74
|
/** The value at the projected path (for rendering) */
|
|
18
75
|
projectedData: ComputedRef<unknown>;
|
|
@@ -23,6 +80,10 @@ export interface ProjectionResult {
|
|
|
23
80
|
handleProjectedChange: (path: string, value: unknown) => void;
|
|
24
81
|
/** Whether projection is active */
|
|
25
82
|
hasProjection: boolean;
|
|
83
|
+
/** Resolved display label (options.label → projected schema title → control.label) */
|
|
84
|
+
projectedLabel: ComputedRef<string>;
|
|
85
|
+
/** Error string combining base-path and projected sub-path errors */
|
|
86
|
+
projectedErrors: ComputedRef<string>;
|
|
26
87
|
}
|
|
27
88
|
|
|
28
89
|
/**
|
|
@@ -44,14 +105,32 @@ export function useProjection(
|
|
|
44
105
|
| undefined;
|
|
45
106
|
|
|
46
107
|
if (!projection) {
|
|
108
|
+
const label = computed(() => resolveLabel(control.value));
|
|
47
109
|
return {
|
|
48
110
|
projectedData: computed(() => control.value.data),
|
|
49
111
|
projectedSchema: computed(() => control.value.schema),
|
|
50
112
|
handleProjectedChange: handleChange,
|
|
51
113
|
hasProjection: false,
|
|
114
|
+
projectedLabel: label,
|
|
115
|
+
projectedErrors: computed(() =>
|
|
116
|
+
prefixErrors(
|
|
117
|
+
label.value.replace(/\*$/, "").trim(),
|
|
118
|
+
normalizeErrors(control.value.errors),
|
|
119
|
+
),
|
|
120
|
+
),
|
|
52
121
|
};
|
|
53
122
|
}
|
|
54
123
|
|
|
124
|
+
// Inject JSONForms state to access raw AJV errors for projected sub-paths.
|
|
125
|
+
// control.errors only contains errors at the exact control path (e.g. "data_rates"),
|
|
126
|
+
// but projected fields need errors at the full path (e.g. "data_rates.0.video_rate_usd").
|
|
127
|
+
const jsonforms = inject<{ core?: { errors?: ErrorLike[] } } | null>(
|
|
128
|
+
"jsonforms",
|
|
129
|
+
null,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const fullProjectedPath = control.value.path + "." + projection;
|
|
133
|
+
|
|
55
134
|
const projectedData = computed(() =>
|
|
56
135
|
getProjectedValue(control.value.data, projection),
|
|
57
136
|
);
|
|
@@ -60,6 +139,32 @@ export function useProjection(
|
|
|
60
139
|
getProjectedSchema(control.value.schema, projection),
|
|
61
140
|
);
|
|
62
141
|
|
|
142
|
+
const label = computed(() =>
|
|
143
|
+
resolveLabel(control.value, projectedSchema.value?.title),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const projectedErrors = computed(() => {
|
|
147
|
+
const baseErrors = normalizeErrors(control.value.errors || "");
|
|
148
|
+
|
|
149
|
+
const rawErrors = jsonforms?.core?.errors ?? [];
|
|
150
|
+
const matching = rawErrors.filter(
|
|
151
|
+
(err) => getErrorPath(err) === fullProjectedPath,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
let errStr: string;
|
|
155
|
+
if (matching.length === 0) {
|
|
156
|
+
errStr = baseErrors;
|
|
157
|
+
} else {
|
|
158
|
+
const projMsg = matching
|
|
159
|
+
.map((e) => (e.keyword === "required" ? "is required" : e.message))
|
|
160
|
+
.filter(Boolean)
|
|
161
|
+
.join("\n");
|
|
162
|
+
errStr = [baseErrors, projMsg].filter(Boolean).join("\n");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return prefixErrors(label.value.replace(/\*$/, "").trim(), errStr);
|
|
166
|
+
});
|
|
167
|
+
|
|
63
168
|
const handleProjectedChange = (path: string, value: unknown) => {
|
|
64
169
|
const fullValue = setProjectedValue(control.value.data, projection, value);
|
|
65
170
|
handleChange(path, fullValue);
|
|
@@ -70,5 +175,7 @@ export function useProjection(
|
|
|
70
175
|
projectedSchema,
|
|
71
176
|
handleProjectedChange,
|
|
72
177
|
hasProjection: true,
|
|
178
|
+
projectedLabel: label,
|
|
179
|
+
projectedErrors,
|
|
73
180
|
};
|
|
74
181
|
}
|
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
|
|
|
@@ -49,10 +49,15 @@ 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
|
-
const { showErrors, markDirty } = useDirtyValidation(control);
|
|
60
|
+
const { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);
|
|
56
61
|
|
|
57
62
|
const onToggle = (val: boolean) => {
|
|
58
63
|
markDirty();
|
|
@@ -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="
|
|
74
|
-
<small v-if="showErrors" class="p-error">{{
|
|
78
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
79
|
+
<small v-if="showErrors" class="p-error">{{ projectedErrors }}</small>
|
|
75
80
|
</div>
|
|
76
81
|
</template>
|
|
@@ -54,7 +54,12 @@ import Dropdown from "primevue/dropdown";
|
|
|
54
54
|
const instance = getCurrentInstance()!;
|
|
55
55
|
const props = instance.props as unknown as ControlProps;
|
|
56
56
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
57
|
-
const {
|
|
57
|
+
const {
|
|
58
|
+
projectedData,
|
|
59
|
+
handleProjectedChange: handleChange,
|
|
60
|
+
projectedErrors,
|
|
61
|
+
projectedLabel,
|
|
62
|
+
} = useProjection(control, rawHandleChange);
|
|
58
63
|
|
|
59
64
|
type Opt = { label: string; value: unknown };
|
|
60
65
|
const toOptions = (schema?: JsonSchema): Opt[] => {
|
|
@@ -158,11 +163,11 @@ watch(
|
|
|
158
163
|
handleChange(control.value.path, valueToSelect);
|
|
159
164
|
}
|
|
160
165
|
},
|
|
161
|
-
{ immediate: true }
|
|
166
|
+
{ immediate: true },
|
|
162
167
|
);
|
|
163
168
|
|
|
164
169
|
// Track user interaction — errors only show after blur
|
|
165
|
-
const { showErrors, markDirty } = useDirtyValidation(control);
|
|
170
|
+
const { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);
|
|
166
171
|
|
|
167
172
|
const onSelect = (val: unknown) => {
|
|
168
173
|
handleChange(control.value.path, val);
|
|
@@ -170,15 +175,13 @@ const onSelect = (val: unknown) => {
|
|
|
170
175
|
</script>
|
|
171
176
|
|
|
172
177
|
<template>
|
|
173
|
-
<div class="
|
|
174
|
-
<label v-if="
|
|
175
|
-
|
|
176
|
-
}}</label>
|
|
177
|
-
<div v-if="control.description" class="text-color-secondary text-left">
|
|
178
|
+
<div class="jf-control">
|
|
179
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
180
|
+
<div v-if="control.description" class="jf-description">
|
|
178
181
|
{{ control.description }}
|
|
179
182
|
</div>
|
|
180
183
|
<Dropdown
|
|
181
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
184
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
182
185
|
:options="options"
|
|
183
186
|
option-label="label"
|
|
184
187
|
option-value="value"
|
|
@@ -193,6 +196,6 @@ const onSelect = (val: unknown) => {
|
|
|
193
196
|
<small v-if="error" class="p-error" role="alert"
|
|
194
197
|
>Failed to load: {{ error }}</small
|
|
195
198
|
>
|
|
196
|
-
<small v-else-if="showErrors" class="p-error">{{
|
|
199
|
+
<small v-else-if="showErrors" class="p-error">{{ projectedErrors }}</small>
|
|
197
200
|
</div>
|
|
198
201
|
</template>
|
|
@@ -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[] => {
|
|
@@ -134,7 +139,7 @@ const options = computed(() => {
|
|
|
134
139
|
useDerive({ control, handleChange, data: projectedData });
|
|
135
140
|
|
|
136
141
|
// Track user interaction — errors only show after first change
|
|
137
|
-
const { showErrors, markDirty } = useDirtyValidation(control);
|
|
142
|
+
const { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);
|
|
138
143
|
|
|
139
144
|
// Auto-select when provider returns only one item (opt-in for multiselect)
|
|
140
145
|
watch(
|
|
@@ -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="
|
|
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
|
-
control.errors
|
|
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
|
() =>
|
|
@@ -102,7 +107,7 @@ const useGrouping = computed(() => {
|
|
|
102
107
|
});
|
|
103
108
|
|
|
104
109
|
// Track user interaction — errors only show after blur
|
|
105
|
-
const { showErrors, markDirty } = useDirtyValidation(control);
|
|
110
|
+
const { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);
|
|
106
111
|
|
|
107
112
|
const onNumber = (val: number | null) => {
|
|
108
113
|
handleChange(control.value.path, val ?? undefined);
|
|
@@ -110,16 +115,14 @@ const onNumber = (val: number | null) => {
|
|
|
110
115
|
</script>
|
|
111
116
|
|
|
112
117
|
<template>
|
|
113
|
-
<div class="
|
|
114
|
-
<label v-if="
|
|
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"
|
|
@@ -132,6 +135,6 @@ const onNumber = (val: number | null) => {
|
|
|
132
135
|
@update:model-value="onNumber"
|
|
133
136
|
@blur="markDirty"
|
|
134
137
|
/>
|
|
135
|
-
<small v-if="showErrors" class="p-error">{{
|
|
138
|
+
<small v-if="showErrors" class="p-error">{{ projectedErrors }}</small>
|
|
136
139
|
</div>
|
|
137
140
|
</template>
|
|
@@ -52,7 +52,12 @@ import AutoComplete from "primevue/autocomplete";
|
|
|
52
52
|
const instance = getCurrentInstance()!;
|
|
53
53
|
const props = instance.props as unknown as ControlProps;
|
|
54
54
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
55
|
-
const {
|
|
55
|
+
const {
|
|
56
|
+
projectedData,
|
|
57
|
+
handleProjectedChange: handleChange,
|
|
58
|
+
projectedErrors,
|
|
59
|
+
projectedLabel,
|
|
60
|
+
} = useProjection(control, rawHandleChange);
|
|
56
61
|
|
|
57
62
|
// Provider support for autocomplete functionality
|
|
58
63
|
const binding = computed(() => {
|
|
@@ -119,7 +124,7 @@ const isAutocomplete = computed(() => !!binding.value);
|
|
|
119
124
|
useDerive({ control, handleChange, data: projectedData });
|
|
120
125
|
|
|
121
126
|
// Track user interaction — errors only show after blur
|
|
122
|
-
const { showErrors, markDirty } = useDirtyValidation(control);
|
|
127
|
+
const { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);
|
|
123
128
|
|
|
124
129
|
function onInput(val: string | undefined) {
|
|
125
130
|
// Convert empty strings to undefined for proper required field validation
|
|
@@ -150,16 +155,14 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
150
155
|
</script>
|
|
151
156
|
|
|
152
157
|
<template>
|
|
153
|
-
<div class="
|
|
154
|
-
<label v-if="
|
|
155
|
-
|
|
156
|
-
}}</label>
|
|
157
|
-
<div v-if="control.description" class="text-color-secondary text-left">
|
|
158
|
+
<div class="jf-control">
|
|
159
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
160
|
+
<div v-if="control.description" class="jf-description">
|
|
158
161
|
{{ control.description }}
|
|
159
162
|
</div>
|
|
160
163
|
<AutoComplete
|
|
161
164
|
v-if="isAutocomplete"
|
|
162
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
165
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
163
166
|
:model-value="projectedData ?? ''"
|
|
164
167
|
:suggestions="items"
|
|
165
168
|
option-label="label"
|
|
@@ -173,7 +176,7 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
173
176
|
/>
|
|
174
177
|
<InputText
|
|
175
178
|
v-else
|
|
176
|
-
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
179
|
+
:class="['w-full!', { 'p-invalid': showErrors }]"
|
|
177
180
|
:model-value="(projectedData as string) ?? ''"
|
|
178
181
|
:disabled="!control.enabled"
|
|
179
182
|
:aria-invalid="showErrors || undefined"
|
|
@@ -185,6 +188,6 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
185
188
|
@blur="onBlur"
|
|
186
189
|
/>
|
|
187
190
|
<small v-if="error" class="p-error" role="alert">Failed: {{ error }}</small>
|
|
188
|
-
<small v-else-if="showErrors" class="p-error">{{
|
|
191
|
+
<small v-else-if="showErrors" class="p-error">{{ projectedErrors }}</small>
|
|
189
192
|
</div>
|
|
190
193
|
</template>
|
|
@@ -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
|
() =>
|
|
@@ -58,7 +63,7 @@ const placeholder = computed<string | undefined>(
|
|
|
58
63
|
);
|
|
59
64
|
|
|
60
65
|
// Track user interaction — errors only show after blur
|
|
61
|
-
const { showErrors, markDirty } = useDirtyValidation(control);
|
|
66
|
+
const { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);
|
|
62
67
|
|
|
63
68
|
function onInput(val: string | undefined) {
|
|
64
69
|
// Convert empty strings to undefined for proper required field validation
|
|
@@ -79,15 +84,13 @@ function onBlur() {
|
|
|
79
84
|
</script>
|
|
80
85
|
|
|
81
86
|
<template>
|
|
82
|
-
<div class="
|
|
83
|
-
<label v-if="
|
|
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"
|
|
@@ -97,6 +100,6 @@ function onBlur() {
|
|
|
97
100
|
@update:model-value="onInput"
|
|
98
101
|
@blur="onBlur"
|
|
99
102
|
/>
|
|
100
|
-
<small v-if="showErrors" class="p-error">{{
|
|
103
|
+
<small v-if="showErrors" class="p-error">{{ projectedErrors }}</small>
|
|
101
104
|
</div>
|
|
102
105
|
</template>
|