@narrative.io/jsonforms-provider-protocols 2.10.0 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -30
- package/dist/core/projection.d.ts +32 -0
- package/dist/core/projection.d.ts.map +1 -0
- package/dist/core/projection.js +74 -0
- package/dist/core/projection.js.map +1 -0
- package/dist/core/resolveScope.d.ts +11 -0
- package/dist/core/resolveScope.d.ts.map +1 -0
- package/dist/core/resolveScope.js +22 -0
- package/dist/core/resolveScope.js.map +1 -0
- package/dist/core/transforms.d.ts +8 -10
- package/dist/core/transforms.d.ts.map +1 -1
- package/dist/core/transforms.js +56 -13
- package/dist/core/transforms.js.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.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 +4 -2
- 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 +6 -4
- 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 +5 -3
- package/dist/vue/components/ProviderSelect.vue2.js.map +1 -1
- package/dist/vue/composables/useDataLayer.d.ts +9 -0
- package/dist/vue/composables/useDataLayer.d.ts.map +1 -0
- package/dist/vue/composables/useDataLayer.js +25 -0
- package/dist/vue/composables/useDataLayer.js.map +1 -0
- package/dist/vue/composables/useDerive.d.ts +5 -2
- package/dist/vue/composables/useDerive.d.ts.map +1 -1
- package/dist/vue/composables/useDerive.js +12 -12
- package/dist/vue/composables/useDerive.js.map +1 -1
- package/dist/vue/composables/useProjection.d.ts +35 -0
- package/dist/vue/composables/useProjection.d.ts.map +1 -0
- package/dist/vue/composables/useProjection.js +33 -0
- package/dist/vue/composables/useProjection.js.map +1 -0
- package/dist/vue/index.d.ts +4 -0
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +15 -29
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/primevue/JfBoolean.vue.d.ts +9 -0
- package/dist/vue/primevue/JfBoolean.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfBoolean.vue.js +10 -5
- package/dist/vue/primevue/JfBoolean.vue.js.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.d.ts +9 -0
- package/dist/vue/primevue/JfEnum.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.js +12 -7
- package/dist/vue/primevue/JfEnum.vue.js.map +1 -1
- package/dist/vue/primevue/JfEnumArray.vue.d.ts +9 -0
- 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 +9 -0
- 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 +9 -0
- package/dist/vue/primevue/JfText.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfText.vue.js +13 -8
- package/dist/vue/primevue/JfText.vue.js.map +1 -1
- package/dist/vue/primevue/JfTextArea.vue.d.ts +9 -0
- package/dist/vue/primevue/JfTextArea.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfTextArea.vue.js +11 -6
- 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 +22 -7
- package/dist/vue/primevue/index.js.map +1 -1
- package/package.json +7 -3
- package/src/core/projection.ts +136 -0
- package/src/core/resolveScope.ts +39 -0
- package/src/core/transforms.ts +91 -26
- package/src/core/types.ts +8 -0
- package/src/index.ts +7 -0
- package/src/vue/components/ProviderAutocomplete.vue +4 -2
- package/src/vue/components/ProviderMultiSelect.vue +6 -4
- package/src/vue/components/ProviderSelect.vue +5 -3
- package/src/vue/composables/useDataLayer.ts +43 -0
- package/src/vue/composables/useDerive.ts +19 -16
- package/src/vue/composables/useProjection.ts +74 -0
- package/src/vue/index.ts +20 -46
- package/src/vue/primevue/JfBoolean.vue +7 -2
- package/src/vue/primevue/JfEnum.vue +9 -4
- package/src/vue/primevue/JfEnumArray.vue +10 -5
- package/src/vue/primevue/JfNumber.vue +8 -3
- package/src/vue/primevue/JfText.vue +10 -5
- package/src/vue/primevue/JfTextArea.vue +8 -3
- package/src/vue/primevue/index.ts +32 -7
package/src/vue/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
rankWith,
|
|
8
8
|
isControl,
|
|
9
9
|
} from "@jsonforms/core";
|
|
10
|
+
import { resolveScopeSchema } from "../core/resolveScope";
|
|
10
11
|
import ProviderAutocomplete from "./components/ProviderAutocomplete.vue";
|
|
11
12
|
import ProviderSelect from "./components/ProviderSelect.vue";
|
|
12
13
|
import ProviderMultiSelect from "./components/ProviderMultiSelect.vue";
|
|
@@ -16,29 +17,21 @@ const hasProvider = (uischema: UISchemaElement) => {
|
|
|
16
17
|
return uischema?.options?.provider !== undefined;
|
|
17
18
|
};
|
|
18
19
|
|
|
20
|
+
// Integer fallback tester — handles nested scopes like #/properties/parent/properties/child
|
|
21
|
+
const isIntegerScope = (uischema: unknown, schema: unknown) => {
|
|
22
|
+
const ui = uischema as { type?: string; scope?: string };
|
|
23
|
+
if (ui?.type !== "Control" || !ui?.scope) return false;
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
+
const propertySchema = resolveScopeSchema(ui.scope, schema as Record<string, any>);
|
|
27
|
+
return propertySchema?.type === "integer";
|
|
28
|
+
};
|
|
29
|
+
|
|
19
30
|
// Create specific testers for each component type
|
|
20
31
|
const providerSelectTester = rankWith(
|
|
21
32
|
106, // Higher than PrimeVue base (100) to ensure providers take precedence
|
|
22
33
|
and(
|
|
23
|
-
or(
|
|
24
|
-
isStringControl,
|
|
25
|
-
isNumberControl,
|
|
26
|
-
and(isControl, (uischema: unknown, schema: unknown) => {
|
|
27
|
-
const ui = uischema as { type?: string; scope?: string };
|
|
28
|
-
const rootSchema = schema as {
|
|
29
|
-
properties?: Record<string, { type?: string }>;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
if (ui?.type !== "Control" || !ui?.scope || !rootSchema?.properties)
|
|
33
|
-
return false;
|
|
34
|
-
|
|
35
|
-
// Extract property name from scope (e.g., "#/properties/age" -> "age")
|
|
36
|
-
const propertyName = ui.scope.replace("#/properties/", "");
|
|
37
|
-
const propertySchema = rootSchema.properties[propertyName];
|
|
38
|
-
|
|
39
|
-
return propertySchema?.type === "integer";
|
|
40
|
-
}),
|
|
41
|
-
),
|
|
34
|
+
or(isStringControl, isNumberControl, and(isControl, isIntegerScope)),
|
|
42
35
|
hasProvider,
|
|
43
36
|
(uischema) => !uischema?.options?.autocomplete,
|
|
44
37
|
),
|
|
@@ -47,44 +40,21 @@ const providerSelectTester = rankWith(
|
|
|
47
40
|
const providerAutocompleteTester = rankWith(
|
|
48
41
|
107, // Higher than PrimeVue base (100) to ensure providers take precedence
|
|
49
42
|
and(
|
|
50
|
-
or(
|
|
51
|
-
isStringControl,
|
|
52
|
-
isNumberControl,
|
|
53
|
-
and(isControl, (uischema: unknown, schema: unknown) => {
|
|
54
|
-
const ui = uischema as { type?: string; scope?: string };
|
|
55
|
-
const rootSchema = schema as {
|
|
56
|
-
properties?: Record<string, { type?: string }>;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
if (ui?.type !== "Control" || !ui?.scope || !rootSchema?.properties)
|
|
60
|
-
return false;
|
|
61
|
-
|
|
62
|
-
// Extract property name from scope (e.g., "#/properties/age" -> "age")
|
|
63
|
-
const propertyName = ui.scope.replace("#/properties/", "");
|
|
64
|
-
const propertySchema = rootSchema.properties[propertyName];
|
|
65
|
-
|
|
66
|
-
return propertySchema?.type === "integer";
|
|
67
|
-
}),
|
|
68
|
-
),
|
|
43
|
+
or(isStringControl, isNumberControl, and(isControl, isIntegerScope)),
|
|
69
44
|
hasProvider,
|
|
70
45
|
(uischema) => uischema?.options?.autocomplete === true,
|
|
71
46
|
),
|
|
72
47
|
);
|
|
73
48
|
|
|
74
|
-
// Custom array tester -
|
|
49
|
+
// Custom array tester - supports nested scope paths
|
|
75
50
|
const isArrayControl = (uischema: UISchemaElement, schema: unknown) => {
|
|
76
51
|
const controlSchema = uischema as { type: string; scope?: string };
|
|
77
52
|
if (controlSchema.type !== "Control" || !controlSchema.scope) {
|
|
78
53
|
return false;
|
|
79
54
|
}
|
|
80
55
|
|
|
81
|
-
//
|
|
82
|
-
const
|
|
83
|
-
const propertyPath = controlSchema.scope.replace("#/properties/", "");
|
|
84
|
-
const propertySchema = rootSchema?.properties?.[propertyPath] as {
|
|
85
|
-
type?: string;
|
|
86
|
-
};
|
|
87
|
-
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
const propertySchema = resolveScopeSchema(controlSchema.scope, schema as Record<string, any>);
|
|
88
58
|
return propertySchema?.type === "array";
|
|
89
59
|
};
|
|
90
60
|
|
|
@@ -105,6 +75,10 @@ export { primevueRenderers, registerPrimevueRenderers } from "./primevue";
|
|
|
105
75
|
// Export individual components
|
|
106
76
|
export { ProviderAutocomplete, ProviderSelect, ProviderMultiSelect };
|
|
107
77
|
export { useProvider } from "./composables/useProvider";
|
|
78
|
+
export { useProjection } from "./composables/useProjection";
|
|
79
|
+
export type { ProjectionResult } from "./composables/useProjection";
|
|
80
|
+
export { createDataLayer, useDataLayer } from "./composables/useDataLayer";
|
|
81
|
+
export type { DataLayer } from "./composables/useDataLayer";
|
|
108
82
|
export * from "./testers";
|
|
109
83
|
|
|
110
84
|
// Export individual PrimeVue components using lazy evaluation to avoid circular deps
|
|
@@ -21,14 +21,17 @@ export default {
|
|
|
21
21
|
renderers: {
|
|
22
22
|
type: Array,
|
|
23
23
|
required: false,
|
|
24
|
+
default: undefined,
|
|
24
25
|
},
|
|
25
26
|
cells: {
|
|
26
27
|
type: Array,
|
|
27
28
|
required: false,
|
|
29
|
+
default: undefined,
|
|
28
30
|
},
|
|
29
31
|
config: {
|
|
30
32
|
type: Object,
|
|
31
33
|
required: false,
|
|
34
|
+
default: undefined,
|
|
32
35
|
},
|
|
33
36
|
},
|
|
34
37
|
};
|
|
@@ -38,12 +41,14 @@ export default {
|
|
|
38
41
|
import type { ControlProps } from "@jsonforms/vue";
|
|
39
42
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
40
43
|
import { getCurrentInstance } from "vue";
|
|
44
|
+
import { useProjection } from "../composables/useProjection";
|
|
41
45
|
import Checkbox from "primevue/checkbox";
|
|
42
46
|
|
|
43
47
|
// Access props from the component instance
|
|
44
48
|
const instance = getCurrentInstance()!;
|
|
45
49
|
const props = instance.props as unknown as ControlProps;
|
|
46
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
50
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
51
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
47
52
|
|
|
48
53
|
const onToggle = (val: boolean) => handleChange(control.value.path, val);
|
|
49
54
|
</script>
|
|
@@ -52,7 +57,7 @@ const onToggle = (val: boolean) => handleChange(control.value.path, val);
|
|
|
52
57
|
<div class="flex items-center gap-2">
|
|
53
58
|
<Checkbox
|
|
54
59
|
:binary="true"
|
|
55
|
-
:model-value="!!
|
|
60
|
+
:model-value="!!projectedData"
|
|
56
61
|
:disabled="!control.enabled"
|
|
57
62
|
:aria-invalid="!!control.errors || undefined"
|
|
58
63
|
@update:model-value="onToggle"
|
|
@@ -22,14 +22,17 @@ export default {
|
|
|
22
22
|
renderers: {
|
|
23
23
|
type: Array,
|
|
24
24
|
required: false,
|
|
25
|
+
default: undefined,
|
|
25
26
|
},
|
|
26
27
|
cells: {
|
|
27
28
|
type: Array,
|
|
28
29
|
required: false,
|
|
30
|
+
default: undefined,
|
|
29
31
|
},
|
|
30
32
|
config: {
|
|
31
33
|
type: Object,
|
|
32
34
|
required: false,
|
|
35
|
+
default: undefined,
|
|
33
36
|
},
|
|
34
37
|
},
|
|
35
38
|
};
|
|
@@ -42,13 +45,15 @@ import { useJsonFormsControl } from "@jsonforms/vue";
|
|
|
42
45
|
import { computed, ref, inject, getCurrentInstance, watch } from "vue";
|
|
43
46
|
import { useProvider } from "../composables/useProvider";
|
|
44
47
|
import { useDerive } from "../composables/useDerive";
|
|
48
|
+
import { useProjection } from "../composables/useProjection";
|
|
45
49
|
import { shouldAutoSelect } from "../utils/autoSelect";
|
|
46
50
|
import Dropdown from "primevue/dropdown";
|
|
47
51
|
|
|
48
52
|
// Access props from the component instance
|
|
49
53
|
const instance = getCurrentInstance()!;
|
|
50
54
|
const props = instance.props as unknown as ControlProps;
|
|
51
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
55
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
56
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
52
57
|
|
|
53
58
|
type Opt = { label: string; value: unknown };
|
|
54
59
|
const toOptions = (schema?: JsonSchema): Opt[] => {
|
|
@@ -134,7 +139,7 @@ const options = computed(() => {
|
|
|
134
139
|
});
|
|
135
140
|
|
|
136
141
|
// Add derive functionality
|
|
137
|
-
useDerive({ control, handleChange });
|
|
142
|
+
useDerive({ control, handleChange, data: projectedData });
|
|
138
143
|
|
|
139
144
|
// Auto-select when provider returns only one item (enabled by default)
|
|
140
145
|
watch(
|
|
@@ -145,7 +150,7 @@ watch(
|
|
|
145
150
|
control.value.uischema?.options?.autoSelectSingle !== false,
|
|
146
151
|
isLoading,
|
|
147
152
|
items,
|
|
148
|
-
currentValue:
|
|
153
|
+
currentValue: projectedData.value,
|
|
149
154
|
});
|
|
150
155
|
|
|
151
156
|
if (valueToSelect !== null) {
|
|
@@ -182,7 +187,7 @@ const onBlur = () => {
|
|
|
182
187
|
:options="options"
|
|
183
188
|
option-label="label"
|
|
184
189
|
option-value="value"
|
|
185
|
-
:model-value="
|
|
190
|
+
:model-value="projectedData ?? null"
|
|
186
191
|
:placeholder="placeholder"
|
|
187
192
|
:disabled="!control.enabled || loading"
|
|
188
193
|
:aria-invalid="!!showErrors || undefined"
|
|
@@ -21,14 +21,17 @@ export default {
|
|
|
21
21
|
renderers: {
|
|
22
22
|
type: Array,
|
|
23
23
|
required: false,
|
|
24
|
+
default: undefined,
|
|
24
25
|
},
|
|
25
26
|
cells: {
|
|
26
27
|
type: Array,
|
|
27
28
|
required: false,
|
|
29
|
+
default: undefined,
|
|
28
30
|
},
|
|
29
31
|
config: {
|
|
30
32
|
type: Object,
|
|
31
33
|
required: false,
|
|
34
|
+
default: undefined,
|
|
32
35
|
},
|
|
33
36
|
},
|
|
34
37
|
};
|
|
@@ -41,13 +44,15 @@ import { useJsonFormsControl } from "@jsonforms/vue";
|
|
|
41
44
|
import { computed, inject, getCurrentInstance, watch } from "vue";
|
|
42
45
|
import { useProvider } from "../composables/useProvider";
|
|
43
46
|
import { useDerive } from "../composables/useDerive";
|
|
47
|
+
import { useProjection } from "../composables/useProjection";
|
|
44
48
|
import { shouldAutoSelectMulti } from "../utils/autoSelect";
|
|
45
49
|
import MultiSelect from "primevue/multiselect";
|
|
46
50
|
|
|
47
51
|
// Access props from the component instance
|
|
48
52
|
const instance = getCurrentInstance()!;
|
|
49
53
|
const props = instance.props as unknown as ControlProps;
|
|
50
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
54
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
55
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
51
56
|
|
|
52
57
|
type Opt = { label: string; value: unknown };
|
|
53
58
|
const toOptions = (schema?: JsonSchema): Opt[] => {
|
|
@@ -125,7 +130,7 @@ const options = computed(() => {
|
|
|
125
130
|
});
|
|
126
131
|
|
|
127
132
|
// Add derive functionality
|
|
128
|
-
useDerive({ control, handleChange });
|
|
133
|
+
useDerive({ control, handleChange, data: projectedData });
|
|
129
134
|
|
|
130
135
|
// Auto-select when provider returns only one item (opt-in for multiselect)
|
|
131
136
|
watch(
|
|
@@ -136,7 +141,7 @@ watch(
|
|
|
136
141
|
control.value.uischema?.options?.autoSelectSingle === true,
|
|
137
142
|
isLoading,
|
|
138
143
|
items,
|
|
139
|
-
currentValue: Array.isArray(
|
|
144
|
+
currentValue: Array.isArray(projectedData.value) ? projectedData.value : [],
|
|
140
145
|
});
|
|
141
146
|
|
|
142
147
|
if (valueToSelect !== null) {
|
|
@@ -165,13 +170,13 @@ const sameSet = (a: unknown[], b: unknown[]) => {
|
|
|
165
170
|
// v-model with guard to avoid recursive updates
|
|
166
171
|
const model = computed<unknown[]>({
|
|
167
172
|
get() {
|
|
168
|
-
const curr = Array.isArray(
|
|
173
|
+
const curr = Array.isArray(projectedData.value) ? projectedData.value : [];
|
|
169
174
|
// return a fresh copy so PrimeMultiSelect can't mutate JSONForms' array in place
|
|
170
175
|
return [...curr];
|
|
171
176
|
},
|
|
172
177
|
set(val) {
|
|
173
178
|
const next = Array.isArray(val) ? [...val] : [];
|
|
174
|
-
const curr = Array.isArray(
|
|
179
|
+
const curr = Array.isArray(projectedData.value) ? projectedData.value : [];
|
|
175
180
|
if (!sameSet(curr, next)) handleChange(control.value.path, next);
|
|
176
181
|
},
|
|
177
182
|
});
|
|
@@ -21,14 +21,17 @@ export default {
|
|
|
21
21
|
renderers: {
|
|
22
22
|
type: Array,
|
|
23
23
|
required: false,
|
|
24
|
+
default: undefined,
|
|
24
25
|
},
|
|
25
26
|
cells: {
|
|
26
27
|
type: Array,
|
|
27
28
|
required: false,
|
|
29
|
+
default: undefined,
|
|
28
30
|
},
|
|
29
31
|
config: {
|
|
30
32
|
type: Object,
|
|
31
33
|
required: false,
|
|
34
|
+
default: undefined,
|
|
32
35
|
},
|
|
33
36
|
},
|
|
34
37
|
};
|
|
@@ -39,12 +42,14 @@ import type { ControlProps } from "@jsonforms/vue";
|
|
|
39
42
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
40
43
|
import { computed, ref, getCurrentInstance } from "vue";
|
|
41
44
|
import { useDerive } from "../composables/useDerive";
|
|
45
|
+
import { useProjection } from "../composables/useProjection";
|
|
42
46
|
import InputNumber from "primevue/inputnumber";
|
|
43
47
|
|
|
44
48
|
// Access props from the component instance
|
|
45
49
|
const instance = getCurrentInstance()!;
|
|
46
50
|
const props = instance.props as unknown as ControlProps;
|
|
47
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
51
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
52
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
48
53
|
|
|
49
54
|
const options = computed(
|
|
50
55
|
() =>
|
|
@@ -57,7 +62,7 @@ const placeholder = computed<string | undefined>(
|
|
|
57
62
|
);
|
|
58
63
|
|
|
59
64
|
// Add derive functionality
|
|
60
|
-
useDerive({ control, handleChange });
|
|
65
|
+
useDerive({ control, handleChange, data: projectedData });
|
|
61
66
|
|
|
62
67
|
// Currency and decimal configuration
|
|
63
68
|
const mode = computed(() => {
|
|
@@ -125,7 +130,7 @@ const onBlur = () => {
|
|
|
125
130
|
:currency="currency"
|
|
126
131
|
:min-fraction-digits="minFractionDigits"
|
|
127
132
|
:max-fraction-digits="maxFractionDigits"
|
|
128
|
-
:model-value="typeof
|
|
133
|
+
:model-value="typeof projectedData === 'number' ? projectedData : null"
|
|
129
134
|
:placeholder="placeholder"
|
|
130
135
|
:disabled="!control.enabled"
|
|
131
136
|
:aria-invalid="!!showErrors || undefined"
|
|
@@ -21,14 +21,17 @@ export default {
|
|
|
21
21
|
renderers: {
|
|
22
22
|
type: Array,
|
|
23
23
|
required: false,
|
|
24
|
+
default: undefined,
|
|
24
25
|
},
|
|
25
26
|
cells: {
|
|
26
27
|
type: Array,
|
|
27
28
|
required: false,
|
|
29
|
+
default: undefined,
|
|
28
30
|
},
|
|
29
31
|
config: {
|
|
30
32
|
type: Object,
|
|
31
33
|
required: false,
|
|
34
|
+
default: undefined,
|
|
32
35
|
},
|
|
33
36
|
},
|
|
34
37
|
};
|
|
@@ -40,13 +43,15 @@ import { useJsonFormsControl } from "@jsonforms/vue";
|
|
|
40
43
|
import { computed, ref, inject, watch, getCurrentInstance } from "vue";
|
|
41
44
|
import { useProvider } from "../composables/useProvider";
|
|
42
45
|
import { useDerive } from "../composables/useDerive";
|
|
46
|
+
import { useProjection } from "../composables/useProjection";
|
|
43
47
|
import InputText from "primevue/inputtext";
|
|
44
48
|
import AutoComplete from "primevue/autocomplete";
|
|
45
49
|
|
|
46
50
|
// Access props from the component instance
|
|
47
51
|
const instance = getCurrentInstance()!;
|
|
48
52
|
const props = instance.props as unknown as ControlProps;
|
|
49
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
53
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
54
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
50
55
|
|
|
51
56
|
// Provider support for autocomplete functionality
|
|
52
57
|
const binding = computed(() => {
|
|
@@ -110,7 +115,7 @@ const placeholder = computed<string | undefined>(() => {
|
|
|
110
115
|
const isAutocomplete = computed(() => !!binding.value);
|
|
111
116
|
|
|
112
117
|
// Add derive functionality
|
|
113
|
-
useDerive({ control, handleChange });
|
|
118
|
+
useDerive({ control, handleChange, data: projectedData });
|
|
114
119
|
|
|
115
120
|
// Track user interaction
|
|
116
121
|
const hasInteracted = ref(false);
|
|
@@ -121,7 +126,7 @@ const showErrors = computed(() => hasInteracted.value && control.value.errors);
|
|
|
121
126
|
function onInput(val: string | undefined) {
|
|
122
127
|
// Convert empty strings to undefined for proper required field validation
|
|
123
128
|
const newValue = val && val.trim() !== "" ? val : undefined;
|
|
124
|
-
if (
|
|
129
|
+
if (projectedData.value !== newValue) {
|
|
125
130
|
handleChange(control.value.path, newValue);
|
|
126
131
|
}
|
|
127
132
|
}
|
|
@@ -158,7 +163,7 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
158
163
|
<AutoComplete
|
|
159
164
|
v-if="isAutocomplete"
|
|
160
165
|
class="w-full"
|
|
161
|
-
:model-value="
|
|
166
|
+
:model-value="projectedData ?? ''"
|
|
162
167
|
:suggestions="items"
|
|
163
168
|
option-label="label"
|
|
164
169
|
:placeholder="placeholder"
|
|
@@ -173,7 +178,7 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
173
178
|
<InputText
|
|
174
179
|
v-else
|
|
175
180
|
class="w-full"
|
|
176
|
-
:model-value="
|
|
181
|
+
:model-value="(projectedData as string) ?? ''"
|
|
177
182
|
:disabled="!control.enabled"
|
|
178
183
|
:aria-invalid="!!showErrors || undefined"
|
|
179
184
|
:placeholder="placeholder"
|
|
@@ -21,14 +21,17 @@ export default {
|
|
|
21
21
|
renderers: {
|
|
22
22
|
type: Array,
|
|
23
23
|
required: false,
|
|
24
|
+
default: undefined,
|
|
24
25
|
},
|
|
25
26
|
cells: {
|
|
26
27
|
type: Array,
|
|
27
28
|
required: false,
|
|
29
|
+
default: undefined,
|
|
28
30
|
},
|
|
29
31
|
config: {
|
|
30
32
|
type: Object,
|
|
31
33
|
required: false,
|
|
34
|
+
default: undefined,
|
|
32
35
|
},
|
|
33
36
|
},
|
|
34
37
|
};
|
|
@@ -38,12 +41,14 @@ export default {
|
|
|
38
41
|
import type { ControlProps } from "@jsonforms/vue";
|
|
39
42
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
40
43
|
import { computed, ref, getCurrentInstance } from "vue";
|
|
44
|
+
import { useProjection } from "../composables/useProjection";
|
|
41
45
|
import Textarea from "primevue/textarea";
|
|
42
46
|
|
|
43
47
|
// Access props from the component instance
|
|
44
48
|
const instance = getCurrentInstance()!;
|
|
45
49
|
const props = instance.props as unknown as ControlProps;
|
|
46
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
50
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
51
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
47
52
|
|
|
48
53
|
const placeholder = computed<string | undefined>(
|
|
49
54
|
() =>
|
|
@@ -59,7 +64,7 @@ const showErrors = computed(() => hasInteracted.value && control.value.errors);
|
|
|
59
64
|
function onInput(val: string | undefined) {
|
|
60
65
|
// Convert empty strings to undefined for proper required field validation
|
|
61
66
|
const newValue = val && val.trim() !== "" ? val : undefined;
|
|
62
|
-
if (
|
|
67
|
+
if (projectedData.value !== newValue) {
|
|
63
68
|
handleChange(control.value.path, newValue);
|
|
64
69
|
}
|
|
65
70
|
}
|
|
@@ -79,7 +84,7 @@ function onBlur() {
|
|
|
79
84
|
</div>
|
|
80
85
|
<Textarea
|
|
81
86
|
class="w-full"
|
|
82
|
-
:model-value="
|
|
87
|
+
:model-value="(projectedData as string) ?? ''"
|
|
83
88
|
:disabled="!control.enabled"
|
|
84
89
|
:aria-invalid="!!showErrors || undefined"
|
|
85
90
|
:placeholder="placeholder"
|
|
@@ -4,6 +4,8 @@ import JfNumber from "./JfNumber.vue";
|
|
|
4
4
|
import JfEnum from "./JfEnum.vue";
|
|
5
5
|
import JfEnumArray from "./JfEnumArray.vue";
|
|
6
6
|
import JfBoolean from "./JfBoolean.vue";
|
|
7
|
+
import { getProjectedSchema } from "../../core/projection";
|
|
8
|
+
import { resolveScopeSchema } from "../../core/resolveScope";
|
|
7
9
|
|
|
8
10
|
// Auto-inject layout styles
|
|
9
11
|
const injectLayoutStyles = () => {
|
|
@@ -54,6 +56,7 @@ export function registerPrimevueRenderers(jsonformsCore: any): unknown[] {
|
|
|
54
56
|
isNumberControl,
|
|
55
57
|
isIntegerControl,
|
|
56
58
|
and,
|
|
59
|
+
or,
|
|
57
60
|
isControl,
|
|
58
61
|
schemaMatches,
|
|
59
62
|
isBooleanControl,
|
|
@@ -97,27 +100,49 @@ export function registerPrimevueRenderers(jsonformsCore: any): unknown[] {
|
|
|
97
100
|
);
|
|
98
101
|
};
|
|
99
102
|
|
|
103
|
+
// Projection-aware schema check: when options.projection is set,
|
|
104
|
+
// resolve the projected schema and test against it instead of the original
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
106
|
+
const projectedSchemaMatches = (check: (schema: any) => boolean) =>
|
|
107
|
+
(uischema: unknown, schema: unknown): boolean => {
|
|
108
|
+
const ui = uischema as { type?: string; scope?: string; options?: { projection?: string } };
|
|
109
|
+
const projection = ui?.options?.projection;
|
|
110
|
+
if (!projection || ui?.type !== "Control" || !ui?.scope) return false;
|
|
111
|
+
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
const propertySchema = resolveScopeSchema(ui.scope, schema as Record<string, any>);
|
|
114
|
+
if (!propertySchema) return false;
|
|
115
|
+
|
|
116
|
+
return check(getProjectedSchema(propertySchema, projection));
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const isMultilineProjection = (uischema: unknown, schema: unknown) => {
|
|
120
|
+
const ui = uischema as { options?: { multi?: boolean } };
|
|
121
|
+
return ui?.options?.multi === true &&
|
|
122
|
+
projectedSchemaMatches((s) => s?.type === "string")(uischema, schema);
|
|
123
|
+
};
|
|
124
|
+
|
|
100
125
|
const renderers = [
|
|
101
126
|
// Multiline text has higher priority than regular text
|
|
102
|
-
{ tester: rankWith(PRIME + 4, isMultilineString), renderer: JfTextArea },
|
|
103
|
-
{ tester: rankWith(PRIME + 3, isStringControl), renderer: JfText },
|
|
127
|
+
{ tester: rankWith(PRIME + 4, or(isMultilineString, isMultilineProjection)), renderer: JfTextArea },
|
|
128
|
+
{ tester: rankWith(PRIME + 3, or(isStringControl, projectedSchemaMatches((s) => s?.type === "string"))), renderer: JfText },
|
|
104
129
|
{
|
|
105
|
-
tester: rankWith(PRIME + 6, isIntegerControl),
|
|
130
|
+
tester: rankWith(PRIME + 6, or(isIntegerControl, projectedSchemaMatches((s) => s?.type === "integer"))),
|
|
106
131
|
renderer: JfNumber,
|
|
107
132
|
},
|
|
108
133
|
{
|
|
109
|
-
tester: rankWith(PRIME + 4, isNumberControl),
|
|
134
|
+
tester: rankWith(PRIME + 4, or(isNumberControl, projectedSchemaMatches((s) => s?.type === "number"))),
|
|
110
135
|
renderer: JfNumber,
|
|
111
136
|
},
|
|
112
137
|
{
|
|
113
|
-
tester: rankWith(PRIME + 7, and(isControl, schemaMatches(isScalarEnum))),
|
|
138
|
+
tester: rankWith(PRIME + 7, or(and(isControl, schemaMatches(isScalarEnum)), and(isControl, projectedSchemaMatches(isScalarEnum)))),
|
|
114
139
|
renderer: JfEnum,
|
|
115
140
|
},
|
|
116
141
|
{
|
|
117
|
-
tester: rankWith(PRIME + 8, and(isControl, schemaMatches(isEnumArray))),
|
|
142
|
+
tester: rankWith(PRIME + 8, or(and(isControl, schemaMatches(isEnumArray)), and(isControl, projectedSchemaMatches(isEnumArray)))),
|
|
118
143
|
renderer: JfEnumArray,
|
|
119
144
|
},
|
|
120
|
-
{ tester: rankWith(PRIME + 3, isBooleanControl), renderer: JfBoolean },
|
|
145
|
+
{ tester: rankWith(PRIME + 3, or(isBooleanControl, projectedSchemaMatches((s) => s?.type === "boolean"))), renderer: JfBoolean },
|
|
121
146
|
];
|
|
122
147
|
|
|
123
148
|
// Update the exported array
|