@narrative.io/jsonforms-provider-protocols 2.10.0 → 3.0.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/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/useDirtyValidation.d.ts +9 -0
- package/dist/vue/composables/useDirtyValidation.d.ts.map +1 -0
- package/dist/vue/composables/useDirtyValidation.js +15 -0
- package/dist/vue/composables/useDirtyValidation.js.map +1 -0
- 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 +5 -0
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +17 -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 +21 -10
- 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 +20 -18
- 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 +24 -14
- 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 +20 -18
- 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 +29 -28
- 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 +23 -14
- 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/useDirtyValidation.ts +15 -0
- package/src/vue/composables/useProjection.ts +74 -0
- package/src/vue/index.ts +21 -46
- package/src/vue/primevue/JfBoolean.vue +18 -5
- package/src/vue/primevue/JfEnum.vue +16 -16
- package/src/vue/primevue/JfEnumArray.vue +21 -9
- package/src/vue/primevue/JfNumber.vue +16 -16
- package/src/vue/primevue/JfText.vue +22 -22
- package/src/vue/primevue/JfTextArea.vue +20 -11
- package/src/vue/primevue/index.ts +32 -7
|
@@ -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,16 @@ 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";
|
|
47
|
+
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
43
48
|
import InputText from "primevue/inputtext";
|
|
44
49
|
import AutoComplete from "primevue/autocomplete";
|
|
45
50
|
|
|
46
51
|
// Access props from the component instance
|
|
47
52
|
const instance = getCurrentInstance()!;
|
|
48
53
|
const props = instance.props as unknown as ControlProps;
|
|
49
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
54
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
55
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
50
56
|
|
|
51
57
|
// Provider support for autocomplete functionality
|
|
52
58
|
const binding = computed(() => {
|
|
@@ -110,32 +116,28 @@ const placeholder = computed<string | undefined>(() => {
|
|
|
110
116
|
const isAutocomplete = computed(() => !!binding.value);
|
|
111
117
|
|
|
112
118
|
// Add derive functionality
|
|
113
|
-
useDerive({ control, handleChange });
|
|
119
|
+
useDerive({ control, handleChange, data: projectedData });
|
|
114
120
|
|
|
115
|
-
// Track user interaction
|
|
116
|
-
const
|
|
117
|
-
const hasFocused = ref(false);
|
|
118
|
-
|
|
119
|
-
const showErrors = computed(() => hasInteracted.value && control.value.errors);
|
|
121
|
+
// Track user interaction — errors only show after blur
|
|
122
|
+
const { showErrors, markDirty } = useDirtyValidation(control);
|
|
120
123
|
|
|
121
124
|
function onInput(val: string | undefined) {
|
|
122
125
|
// Convert empty strings to undefined for proper required field validation
|
|
123
126
|
const newValue = val && val.trim() !== "" ? val : undefined;
|
|
124
|
-
if (
|
|
127
|
+
if (projectedData.value !== newValue) {
|
|
125
128
|
handleChange(control.value.path, newValue);
|
|
126
129
|
}
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
function onBlur() {
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
markDirty();
|
|
134
|
+
// Normalize empty strings to undefined so required validation fires
|
|
135
|
+
const val = projectedData.value;
|
|
136
|
+
if (typeof val === "string" && val.trim() === "") {
|
|
137
|
+
handleChange(control.value.path, undefined);
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
|
|
135
|
-
function onFocus() {
|
|
136
|
-
hasFocused.value = true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
141
|
// Autocomplete specific handlers
|
|
140
142
|
const onComplete = (event: { query: string }) => {
|
|
141
143
|
query.value = event.query;
|
|
@@ -157,32 +159,30 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
157
159
|
</div>
|
|
158
160
|
<AutoComplete
|
|
159
161
|
v-if="isAutocomplete"
|
|
160
|
-
class="w-full"
|
|
161
|
-
:model-value="
|
|
162
|
+
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
163
|
+
:model-value="projectedData ?? ''"
|
|
162
164
|
:suggestions="items"
|
|
163
165
|
option-label="label"
|
|
164
166
|
:placeholder="placeholder"
|
|
165
167
|
:disabled="!control.enabled"
|
|
166
|
-
:aria-invalid="
|
|
168
|
+
:aria-invalid="showErrors || undefined"
|
|
167
169
|
@complete="onComplete"
|
|
168
170
|
@item-select="onSelect"
|
|
169
171
|
@update:model-value="onInput"
|
|
170
172
|
@blur="onBlur"
|
|
171
|
-
@focus="onFocus"
|
|
172
173
|
/>
|
|
173
174
|
<InputText
|
|
174
175
|
v-else
|
|
175
|
-
class="w-full"
|
|
176
|
-
:model-value="
|
|
176
|
+
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
177
|
+
:model-value="(projectedData as string) ?? ''"
|
|
177
178
|
:disabled="!control.enabled"
|
|
178
|
-
:aria-invalid="
|
|
179
|
+
:aria-invalid="showErrors || undefined"
|
|
179
180
|
:placeholder="placeholder"
|
|
180
181
|
autocapitalize="off"
|
|
181
182
|
autocomplete="off"
|
|
182
183
|
spellcheck="false"
|
|
183
184
|
@update:model-value="onInput"
|
|
184
185
|
@blur="onBlur"
|
|
185
|
-
@focus="onFocus"
|
|
186
186
|
/>
|
|
187
187
|
<small v-if="error" class="p-error" role="alert">Failed: {{ error }}</small>
|
|
188
188
|
<small v-else-if="showErrors" class="p-error">{{ control.errors }}</small>
|
|
@@ -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
|
};
|
|
@@ -37,13 +40,16 @@ export default {
|
|
|
37
40
|
<script setup lang="ts">
|
|
38
41
|
import type { ControlProps } from "@jsonforms/vue";
|
|
39
42
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
40
|
-
import { computed,
|
|
43
|
+
import { computed, getCurrentInstance } from "vue";
|
|
44
|
+
import { useProjection } from "../composables/useProjection";
|
|
45
|
+
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
41
46
|
import Textarea from "primevue/textarea";
|
|
42
47
|
|
|
43
48
|
// Access props from the component instance
|
|
44
49
|
const instance = getCurrentInstance()!;
|
|
45
50
|
const props = instance.props as unknown as ControlProps;
|
|
46
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
51
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
52
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
47
53
|
|
|
48
54
|
const placeholder = computed<string | undefined>(
|
|
49
55
|
() =>
|
|
@@ -51,21 +57,24 @@ const placeholder = computed<string | undefined>(
|
|
|
51
57
|
?.placeholder ?? control.value.description,
|
|
52
58
|
);
|
|
53
59
|
|
|
54
|
-
// Track user interaction
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
const showErrors = computed(() => hasInteracted.value && control.value.errors);
|
|
60
|
+
// Track user interaction — errors only show after blur
|
|
61
|
+
const { showErrors, markDirty } = useDirtyValidation(control);
|
|
58
62
|
|
|
59
63
|
function onInput(val: string | undefined) {
|
|
60
64
|
// Convert empty strings to undefined for proper required field validation
|
|
61
65
|
const newValue = val && val.trim() !== "" ? val : undefined;
|
|
62
|
-
if (
|
|
66
|
+
if (projectedData.value !== newValue) {
|
|
63
67
|
handleChange(control.value.path, newValue);
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
function onBlur() {
|
|
68
|
-
|
|
72
|
+
markDirty();
|
|
73
|
+
// Normalize empty strings to undefined so required validation fires
|
|
74
|
+
const val = projectedData.value;
|
|
75
|
+
if (typeof val === "string" && val.trim() === "") {
|
|
76
|
+
handleChange(control.value.path, undefined);
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
</script>
|
|
71
80
|
|
|
@@ -78,10 +87,10 @@ function onBlur() {
|
|
|
78
87
|
{{ control.description }}
|
|
79
88
|
</div>
|
|
80
89
|
<Textarea
|
|
81
|
-
class="w-full"
|
|
82
|
-
:model-value="
|
|
90
|
+
:class="['w-full', { 'p-invalid': showErrors }]"
|
|
91
|
+
:model-value="(projectedData as string) ?? ''"
|
|
83
92
|
:disabled="!control.enabled"
|
|
84
|
-
:aria-invalid="
|
|
93
|
+
:aria-invalid="showErrors || undefined"
|
|
85
94
|
:placeholder="placeholder"
|
|
86
95
|
:rows="4"
|
|
87
96
|
:auto-resize="true"
|
|
@@ -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
|