@narrative.io/jsonforms-provider-protocols 1.1.0-beta.1 → 1.1.0-beta.10
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 +61 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -2
- package/dist/index.js.map +1 -1
- package/dist/jsonforms-provider-protocols.css +0 -4
- package/dist/protocols/rest_api.d.ts +1 -0
- package/dist/protocols/rest_api.d.ts.map +1 -1
- package/dist/protocols/rest_api.js +6 -1
- package/dist/protocols/rest_api.js.map +1 -1
- package/dist/vue/composables/useDerive.d.ts +13 -0
- package/dist/vue/composables/useDerive.d.ts.map +1 -0
- package/dist/vue/composables/useDerive.js +71 -0
- package/dist/vue/composables/useDerive.js.map +1 -0
- package/dist/vue/index.d.ts +1 -1
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +6 -6
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/primevue/JfBoolean.vue.d.ts +20 -32
- package/dist/vue/primevue/JfBoolean.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfBoolean.vue.js +38 -6
- package/dist/vue/primevue/JfBoolean.vue.js.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.d.ts +20 -32
- package/dist/vue/primevue/JfEnum.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.js +152 -5
- package/dist/vue/primevue/JfEnum.vue.js.map +1 -1
- package/dist/vue/primevue/JfEnum.vue2.js +1 -61
- package/dist/vue/primevue/JfEnum.vue2.js.map +1 -1
- package/dist/vue/primevue/JfEnumArray.vue.d.ts +20 -32
- package/dist/vue/primevue/JfEnumArray.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfEnumArray.vue.js +93 -13
- package/dist/vue/primevue/JfEnumArray.vue.js.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.d.ts +20 -32
- package/dist/vue/primevue/JfNumber.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.js +87 -13
- package/dist/vue/primevue/JfNumber.vue.js.map +1 -1
- package/dist/vue/primevue/JfText.vue.d.ts +20 -32
- package/dist/vue/primevue/JfText.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfText.vue.js +131 -16
- package/dist/vue/primevue/JfText.vue.js.map +1 -1
- package/dist/vue/primevue/JfTextArea.vue.d.ts +20 -32
- package/dist/vue/primevue/JfTextArea.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfTextArea.vue.js +49 -11
- package/dist/vue/primevue/JfTextArea.vue.js.map +1 -1
- package/dist/vue/primevue/index.d.ts +2 -75
- package/dist/vue/primevue/index.d.ts.map +1 -1
- package/dist/vue/primevue/index.js +46 -28
- package/dist/vue/primevue/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +15 -2
- package/src/protocols/rest_api.ts +8 -1
- package/src/vue/composables/useDerive.ts +105 -0
- package/src/vue/index.ts +10 -1
- package/src/vue/primevue/JfBoolean.vue +42 -5
- package/src/vue/primevue/JfEnum.vue +131 -20
- package/src/vue/primevue/JfEnumArray.vue +118 -14
- package/src/vue/primevue/JfNumber.vue +104 -13
- package/src/vue/primevue/JfText.vue +156 -13
- package/src/vue/primevue/JfTextArea.vue +57 -10
- package/src/vue/primevue/index.ts +48 -37
- package/src/vue/styles.css +5 -0
|
@@ -1,12 +1,52 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Define props manually to avoid any potential circular dependency issues
|
|
3
|
+
export default {
|
|
4
|
+
name: "JfEnum",
|
|
5
|
+
props: {
|
|
6
|
+
uischema: {
|
|
7
|
+
type: Object,
|
|
8
|
+
required: true,
|
|
9
|
+
},
|
|
10
|
+
schema: {
|
|
11
|
+
type: Object,
|
|
12
|
+
required: true,
|
|
13
|
+
},
|
|
14
|
+
path: {
|
|
15
|
+
type: String,
|
|
16
|
+
required: true,
|
|
17
|
+
},
|
|
18
|
+
enabled: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: undefined,
|
|
21
|
+
},
|
|
22
|
+
renderers: {
|
|
23
|
+
type: Array,
|
|
24
|
+
required: false,
|
|
25
|
+
},
|
|
26
|
+
cells: {
|
|
27
|
+
type: Array,
|
|
28
|
+
required: false,
|
|
29
|
+
},
|
|
30
|
+
config: {
|
|
31
|
+
type: Object,
|
|
32
|
+
required: false,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
</script>
|
|
37
|
+
|
|
1
38
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
39
|
+
import type { JsonSchema } from "@jsonforms/core";
|
|
40
|
+
import type { ControlProps } from "@jsonforms/vue";
|
|
41
|
+
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
42
|
+
import { computed, ref, inject, getCurrentInstance } from "vue";
|
|
43
|
+
import { useProvider } from "../composables/useProvider";
|
|
44
|
+
import { useDerive } from "../composables/useDerive";
|
|
5
45
|
import Dropdown from "primevue/dropdown";
|
|
6
46
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const props =
|
|
47
|
+
// Access props from the component instance
|
|
48
|
+
const instance = getCurrentInstance()!;
|
|
49
|
+
const props = instance.props as unknown as ControlProps;
|
|
10
50
|
const { control, handleChange } = useJsonFormsControl(props);
|
|
11
51
|
|
|
12
52
|
type Opt = { label: string; value: unknown };
|
|
@@ -26,14 +66,87 @@ const toOptions = (schema?: JsonSchema): Opt[] => {
|
|
|
26
66
|
return [];
|
|
27
67
|
};
|
|
28
68
|
|
|
29
|
-
|
|
69
|
+
// Provider support
|
|
70
|
+
const binding = computed(() => {
|
|
71
|
+
const provider = control.value.uischema?.options?.provider;
|
|
72
|
+
// Ensure load property is set to 'mount' by default
|
|
73
|
+
if (provider && typeof provider === "object" && !provider.load) {
|
|
74
|
+
return { ...provider, load: "mount" };
|
|
75
|
+
}
|
|
76
|
+
return provider;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const deps = computed(
|
|
30
80
|
() =>
|
|
31
|
-
(
|
|
32
|
-
|
|
81
|
+
((
|
|
82
|
+
(control.value.schema as Record<string, unknown>)?.[
|
|
83
|
+
"x-provider"
|
|
84
|
+
] as Record<string, unknown>
|
|
85
|
+
)?.dependsOn as string[]) ?? [],
|
|
33
86
|
);
|
|
87
|
+
const depValues = computed(() => {
|
|
88
|
+
return deps.value.map((dep) => {
|
|
89
|
+
// Resolve dependency value from form data using JSON pointer-like path
|
|
90
|
+
const path = dep.startsWith("#/") ? dep.slice(2) : dep;
|
|
91
|
+
const keys = path.replace(/\//g, ".").split(".");
|
|
92
|
+
let value: unknown = rootData.value;
|
|
93
|
+
for (const key of keys) {
|
|
94
|
+
if (value && typeof value === "object" && key in value) {
|
|
95
|
+
value = (value as Record<string, unknown>)[key];
|
|
96
|
+
} else {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Get the root form data from JSONForms context for template URL resolution
|
|
105
|
+
const injectedFormData = inject<{ value: unknown }>("formData", { value: {} });
|
|
106
|
+
const rootData = computed(() => injectedFormData.value || {});
|
|
107
|
+
|
|
108
|
+
// Use provider if available, otherwise fall back to schema enum/oneOf
|
|
109
|
+
const {
|
|
110
|
+
items: providerItems,
|
|
111
|
+
loading,
|
|
112
|
+
error,
|
|
113
|
+
} = useProvider(binding, {
|
|
114
|
+
data: rootData,
|
|
115
|
+
path: control.value.path,
|
|
116
|
+
dependsOnValues: depValues.value,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const placeholder = computed<string | undefined>(() => {
|
|
120
|
+
if (loading.value) return "Loading…";
|
|
121
|
+
return (
|
|
122
|
+
(control.value.uischema as { options?: { placeholder?: string } })?.options
|
|
123
|
+
?.placeholder ?? control.value.description
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const options = computed(() => {
|
|
128
|
+
// Use provider items if available, otherwise fall back to schema enum/oneOf
|
|
129
|
+
if (binding.value && providerItems.value.length > 0) {
|
|
130
|
+
return providerItems.value;
|
|
131
|
+
}
|
|
132
|
+
return toOptions(control.value.schema);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Add derive functionality
|
|
136
|
+
useDerive({ control, handleChange });
|
|
137
|
+
|
|
138
|
+
// Track user interaction
|
|
139
|
+
const hasInteracted = ref(false);
|
|
140
|
+
|
|
141
|
+
const showErrors = computed(() => hasInteracted.value && control.value.errors);
|
|
34
142
|
|
|
35
|
-
const
|
|
36
|
-
|
|
143
|
+
const onSelect = (val: unknown) => {
|
|
144
|
+
handleChange(control.value.path, val);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const onBlur = () => {
|
|
148
|
+
hasInteracted.value = true;
|
|
149
|
+
};
|
|
37
150
|
</script>
|
|
38
151
|
|
|
39
152
|
<template>
|
|
@@ -51,17 +164,15 @@ const onSelect = (val: unknown) => handleChange(control.value.path, val);
|
|
|
51
164
|
option-value="value"
|
|
52
165
|
:model-value="control.data ?? null"
|
|
53
166
|
:placeholder="placeholder"
|
|
54
|
-
:disabled="!control.enabled"
|
|
55
|
-
:aria-invalid="!!
|
|
167
|
+
:disabled="!control.enabled || loading"
|
|
168
|
+
:aria-invalid="!!showErrors || undefined"
|
|
56
169
|
:show-clear="true"
|
|
57
170
|
@update:model-value="onSelect"
|
|
171
|
+
@blur="onBlur"
|
|
58
172
|
/>
|
|
59
|
-
<small v-if="
|
|
173
|
+
<small v-if="error" class="p-error" role="alert"
|
|
174
|
+
>Failed to load: {{ error }}</small
|
|
175
|
+
>
|
|
176
|
+
<small v-else-if="showErrors" class="p-error">{{ control.errors }}</small>
|
|
60
177
|
</div>
|
|
61
178
|
</template>
|
|
62
|
-
|
|
63
|
-
<style scoped>
|
|
64
|
-
:deep(.p-dropdown-label) {
|
|
65
|
-
text-align: left;
|
|
66
|
-
}
|
|
67
|
-
</style>
|
|
@@ -1,12 +1,51 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: "JfEnumArray",
|
|
4
|
+
props: {
|
|
5
|
+
uischema: {
|
|
6
|
+
type: Object,
|
|
7
|
+
required: true,
|
|
8
|
+
},
|
|
9
|
+
schema: {
|
|
10
|
+
type: Object,
|
|
11
|
+
required: true,
|
|
12
|
+
},
|
|
13
|
+
path: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true,
|
|
16
|
+
},
|
|
17
|
+
enabled: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: undefined,
|
|
20
|
+
},
|
|
21
|
+
renderers: {
|
|
22
|
+
type: Array,
|
|
23
|
+
required: false,
|
|
24
|
+
},
|
|
25
|
+
cells: {
|
|
26
|
+
type: Array,
|
|
27
|
+
required: false,
|
|
28
|
+
},
|
|
29
|
+
config: {
|
|
30
|
+
type: Object,
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
36
|
+
|
|
1
37
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
38
|
+
import type { JsonSchema } from "@jsonforms/core";
|
|
39
|
+
import type { ControlProps } from "@jsonforms/vue";
|
|
40
|
+
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
41
|
+
import { computed, inject, getCurrentInstance } from "vue";
|
|
42
|
+
import { useProvider } from "../composables/useProvider";
|
|
43
|
+
import { useDerive } from "../composables/useDerive";
|
|
5
44
|
import MultiSelect from "primevue/multiselect";
|
|
6
45
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const props =
|
|
46
|
+
// Access props from the component instance
|
|
47
|
+
const instance = getCurrentInstance()!;
|
|
48
|
+
const props = instance.props as unknown as ControlProps;
|
|
10
49
|
const { control, handleChange } = useJsonFormsControl(props);
|
|
11
50
|
|
|
12
51
|
type Opt = { label: string; value: unknown };
|
|
@@ -26,14 +65,74 @@ const toOptions = (schema?: JsonSchema): Opt[] => {
|
|
|
26
65
|
return [];
|
|
27
66
|
};
|
|
28
67
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
68
|
+
// Provider support
|
|
69
|
+
const binding = computed(() => {
|
|
70
|
+
const provider = control.value.uischema?.options?.provider;
|
|
71
|
+
// Ensure load property is set to 'mount' by default
|
|
72
|
+
if (provider && typeof provider === "object" && !provider.load) {
|
|
73
|
+
return { ...provider, load: "mount" };
|
|
74
|
+
}
|
|
75
|
+
return provider;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const deps = computed(
|
|
33
79
|
() =>
|
|
34
|
-
(
|
|
35
|
-
|
|
80
|
+
((
|
|
81
|
+
(control.value.schema as Record<string, unknown>)?.[
|
|
82
|
+
"x-provider"
|
|
83
|
+
] as Record<string, unknown>
|
|
84
|
+
)?.dependsOn as string[]) ?? [],
|
|
36
85
|
);
|
|
86
|
+
const depValues = computed(() => {
|
|
87
|
+
return deps.value.map((dep) => {
|
|
88
|
+
// Resolve dependency value from form data using JSON pointer-like path
|
|
89
|
+
const path = dep.startsWith("#/") ? dep.slice(2) : dep;
|
|
90
|
+
const keys = path.replace(/\//g, ".").split(".");
|
|
91
|
+
let value: unknown = rootData.value;
|
|
92
|
+
for (const key of keys) {
|
|
93
|
+
if (value && typeof value === "object" && key in value) {
|
|
94
|
+
value = (value as Record<string, unknown>)[key];
|
|
95
|
+
} else {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Get the root form data from JSONForms context for template URL resolution
|
|
104
|
+
const injectedFormData = inject<{ value: unknown }>("formData", { value: {} });
|
|
105
|
+
const rootData = computed(() => injectedFormData.value || {});
|
|
106
|
+
|
|
107
|
+
// Use provider if available, otherwise fall back to schema enum/oneOf
|
|
108
|
+
const {
|
|
109
|
+
items: providerItems,
|
|
110
|
+
loading,
|
|
111
|
+
error,
|
|
112
|
+
} = useProvider(binding, {
|
|
113
|
+
data: rootData,
|
|
114
|
+
path: control.value.path,
|
|
115
|
+
dependsOnValues: depValues.value,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const options = computed(() => {
|
|
119
|
+
// Use provider items if available, otherwise fall back to schema enum/oneOf
|
|
120
|
+
if (binding.value && providerItems.value.length > 0) {
|
|
121
|
+
return providerItems.value;
|
|
122
|
+
}
|
|
123
|
+
return toOptions((control.value.schema as { items?: JsonSchema })?.items);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Add derive functionality
|
|
127
|
+
useDerive({ control, handleChange });
|
|
128
|
+
|
|
129
|
+
const placeholder = computed<string | undefined>(() => {
|
|
130
|
+
if (loading.value) return "Loading…";
|
|
131
|
+
return (
|
|
132
|
+
(control.value.uischema as { options?: { placeholder?: string } })?.options
|
|
133
|
+
?.placeholder ?? control.value.description
|
|
134
|
+
);
|
|
135
|
+
});
|
|
37
136
|
|
|
38
137
|
// order-insensitive shallow equality for primitive enums
|
|
39
138
|
const sameSet = (a: unknown[], b: unknown[]) => {
|
|
@@ -75,11 +174,16 @@ const model = computed<unknown[]>({
|
|
|
75
174
|
option-value="value"
|
|
76
175
|
data-key="value"
|
|
77
176
|
display="chip"
|
|
78
|
-
:disabled="!control.enabled"
|
|
177
|
+
:disabled="!control.enabled || loading"
|
|
79
178
|
:aria-invalid="!!control.errors || undefined"
|
|
80
179
|
:placeholder="placeholder"
|
|
81
180
|
/>
|
|
82
181
|
|
|
83
|
-
<small v-if="
|
|
182
|
+
<small v-if="error" class="p-error" role="alert"
|
|
183
|
+
>Failed to load: {{ error }}</small
|
|
184
|
+
>
|
|
185
|
+
<small v-else-if="control.errors" class="p-error">{{
|
|
186
|
+
control.errors
|
|
187
|
+
}}</small>
|
|
84
188
|
</div>
|
|
85
189
|
</template>
|
|
@@ -1,22 +1,108 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: "JfNumber",
|
|
4
|
+
props: {
|
|
5
|
+
uischema: {
|
|
6
|
+
type: Object,
|
|
7
|
+
required: true,
|
|
8
|
+
},
|
|
9
|
+
schema: {
|
|
10
|
+
type: Object,
|
|
11
|
+
required: true,
|
|
12
|
+
},
|
|
13
|
+
path: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true,
|
|
16
|
+
},
|
|
17
|
+
enabled: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: undefined,
|
|
20
|
+
},
|
|
21
|
+
renderers: {
|
|
22
|
+
type: Array,
|
|
23
|
+
required: false,
|
|
24
|
+
},
|
|
25
|
+
cells: {
|
|
26
|
+
type: Array,
|
|
27
|
+
required: false,
|
|
28
|
+
},
|
|
29
|
+
config: {
|
|
30
|
+
type: Object,
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
36
|
+
|
|
1
37
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import { computed } from "vue";
|
|
38
|
+
import type { ControlProps } from "@jsonforms/vue";
|
|
39
|
+
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
40
|
+
import { computed, ref, getCurrentInstance } from "vue";
|
|
41
|
+
import { useDerive } from "../composables/useDerive";
|
|
5
42
|
import InputNumber from "primevue/inputnumber";
|
|
6
43
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const props =
|
|
44
|
+
// Access props from the component instance
|
|
45
|
+
const instance = getCurrentInstance()!;
|
|
46
|
+
const props = instance.props as unknown as ControlProps;
|
|
10
47
|
const { control, handleChange } = useJsonFormsControl(props);
|
|
11
48
|
|
|
12
|
-
const
|
|
49
|
+
const options = computed(
|
|
13
50
|
() =>
|
|
14
|
-
(control.value.uischema as { options?:
|
|
15
|
-
?.
|
|
51
|
+
(control.value.uischema as { options?: Record<string, unknown> })
|
|
52
|
+
?.options ?? {},
|
|
16
53
|
);
|
|
17
54
|
|
|
18
|
-
const
|
|
55
|
+
const placeholder = computed<string | undefined>(
|
|
56
|
+
() => (options.value.placeholder as string) ?? control.value.description,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Add derive functionality
|
|
60
|
+
useDerive({ control, handleChange });
|
|
61
|
+
|
|
62
|
+
// Currency and decimal configuration
|
|
63
|
+
const mode = computed(() => {
|
|
64
|
+
if (options.value.currency) return "currency";
|
|
65
|
+
if (options.value.decimal || typeof options.value.precision === "number")
|
|
66
|
+
return "decimal";
|
|
67
|
+
return undefined;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const currency = computed(() =>
|
|
71
|
+
typeof options.value.currency === "string" ? options.value.currency : "USD",
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const minFractionDigits = computed(() => {
|
|
75
|
+
if (mode.value === "currency") return 2;
|
|
76
|
+
if (typeof options.value.precision === "number")
|
|
77
|
+
return options.value.precision;
|
|
78
|
+
return undefined;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const maxFractionDigits = computed(() => {
|
|
82
|
+
if (mode.value === "currency") return 2;
|
|
83
|
+
if (typeof options.value.precision === "number")
|
|
84
|
+
return options.value.precision;
|
|
85
|
+
return undefined;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const useGrouping = computed(() => {
|
|
89
|
+
// Enable grouping for currency by default, or if explicitly set
|
|
90
|
+
if (mode.value === "currency") return true;
|
|
91
|
+
return options.value.useGrouping === true;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Track user interaction
|
|
95
|
+
const hasInteracted = ref(false);
|
|
96
|
+
|
|
97
|
+
const showErrors = computed(() => hasInteracted.value && control.value.errors);
|
|
98
|
+
|
|
99
|
+
const onNumber = (val: number | null) => {
|
|
19
100
|
handleChange(control.value.path, val ?? undefined);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const onBlur = () => {
|
|
104
|
+
hasInteracted.value = true;
|
|
105
|
+
};
|
|
20
106
|
</script>
|
|
21
107
|
|
|
22
108
|
<template>
|
|
@@ -30,13 +116,18 @@ const onNumber = (val: number | null) =>
|
|
|
30
116
|
<InputNumber
|
|
31
117
|
class="w-full"
|
|
32
118
|
input-class="w-full"
|
|
33
|
-
:use-grouping="
|
|
119
|
+
:use-grouping="useGrouping"
|
|
120
|
+
:mode="mode"
|
|
121
|
+
:currency="currency"
|
|
122
|
+
:min-fraction-digits="minFractionDigits"
|
|
123
|
+
:max-fraction-digits="maxFractionDigits"
|
|
34
124
|
:model-value="typeof control.data === 'number' ? control.data : null"
|
|
35
125
|
:placeholder="placeholder"
|
|
36
126
|
:disabled="!control.enabled"
|
|
37
|
-
:aria-invalid="!!
|
|
127
|
+
:aria-invalid="!!showErrors || undefined"
|
|
38
128
|
@update:model-value="onNumber"
|
|
129
|
+
@blur="onBlur"
|
|
39
130
|
/>
|
|
40
|
-
<small v-if="
|
|
131
|
+
<small v-if="showErrors" class="p-error">{{ control.errors }}</small>
|
|
41
132
|
</div>
|
|
42
133
|
</template>
|
|
@@ -1,26 +1,150 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: "JfText",
|
|
4
|
+
props: {
|
|
5
|
+
uischema: {
|
|
6
|
+
type: Object,
|
|
7
|
+
required: true,
|
|
8
|
+
},
|
|
9
|
+
schema: {
|
|
10
|
+
type: Object,
|
|
11
|
+
required: true,
|
|
12
|
+
},
|
|
13
|
+
path: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true,
|
|
16
|
+
},
|
|
17
|
+
enabled: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: undefined,
|
|
20
|
+
},
|
|
21
|
+
renderers: {
|
|
22
|
+
type: Array,
|
|
23
|
+
required: false,
|
|
24
|
+
},
|
|
25
|
+
cells: {
|
|
26
|
+
type: Array,
|
|
27
|
+
required: false,
|
|
28
|
+
},
|
|
29
|
+
config: {
|
|
30
|
+
type: Object,
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
</script>
|
|
36
|
+
|
|
1
37
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import { computed } from "vue";
|
|
38
|
+
import type { ControlProps } from "@jsonforms/vue";
|
|
39
|
+
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
40
|
+
import { computed, ref, inject, watch, getCurrentInstance } from "vue";
|
|
41
|
+
import { useProvider } from "../composables/useProvider";
|
|
42
|
+
import { useDerive } from "../composables/useDerive";
|
|
5
43
|
import InputText from "primevue/inputtext";
|
|
44
|
+
import AutoComplete from "primevue/autocomplete";
|
|
6
45
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const props =
|
|
46
|
+
// Access props from the component instance
|
|
47
|
+
const instance = getCurrentInstance()!;
|
|
48
|
+
const props = instance.props as unknown as ControlProps;
|
|
10
49
|
const { control, handleChange } = useJsonFormsControl(props);
|
|
11
50
|
|
|
12
|
-
|
|
51
|
+
// Provider support for autocomplete functionality
|
|
52
|
+
const binding = computed(() => {
|
|
53
|
+
const provider = control.value.uischema?.options?.provider;
|
|
54
|
+
// Ensure load property is set to 'query' by default for autocomplete
|
|
55
|
+
if (provider && typeof provider === "object" && !provider.load) {
|
|
56
|
+
return { ...provider, load: "query" };
|
|
57
|
+
}
|
|
58
|
+
return provider;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const deps = computed(
|
|
13
62
|
() =>
|
|
14
|
-
(
|
|
15
|
-
|
|
63
|
+
((
|
|
64
|
+
(control.value.schema as Record<string, unknown>)?.[
|
|
65
|
+
"x-provider"
|
|
66
|
+
] as Record<string, unknown>
|
|
67
|
+
)?.dependsOn as string[]) ?? [],
|
|
16
68
|
);
|
|
69
|
+
const depValues = computed(() => {
|
|
70
|
+
return deps.value.map((dep) => {
|
|
71
|
+
// Resolve dependency value from form data using JSON pointer-like path
|
|
72
|
+
const path = dep.startsWith("#/") ? dep.slice(2) : dep;
|
|
73
|
+
const keys = path.replace(/\//g, ".").split(".");
|
|
74
|
+
let value: unknown = rootData.value;
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
if (value && typeof value === "object" && key in value) {
|
|
77
|
+
value = (value as Record<string, unknown>)[key];
|
|
78
|
+
} else {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Get the root form data from JSONForms context for template URL resolution
|
|
87
|
+
const injectedFormData = inject<{ value: unknown }>("formData", { value: {} });
|
|
88
|
+
const rootData = computed(() => injectedFormData.value || {});
|
|
89
|
+
|
|
90
|
+
const query = ref("");
|
|
91
|
+
const { items, loading, error, reload } = useProvider(binding, {
|
|
92
|
+
data: rootData,
|
|
93
|
+
path: control.value.path,
|
|
94
|
+
uiQuery: query.value,
|
|
95
|
+
dependsOnValues: depValues.value,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
watch(query, () => {
|
|
99
|
+
if (binding.value?.load === "query") reload();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const placeholder = computed<string | undefined>(() => {
|
|
103
|
+
if (loading.value) return "Loading…";
|
|
104
|
+
return (
|
|
105
|
+
(control.value.uischema as { options?: { placeholder?: string } })?.options
|
|
106
|
+
?.placeholder ?? control.value.description
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const isAutocomplete = computed(() => !!binding.value);
|
|
111
|
+
|
|
112
|
+
// Add derive functionality
|
|
113
|
+
useDerive({ control, handleChange });
|
|
114
|
+
|
|
115
|
+
// Track user interaction
|
|
116
|
+
const hasInteracted = ref(false);
|
|
117
|
+
const hasFocused = ref(false);
|
|
118
|
+
|
|
119
|
+
const showErrors = computed(() => hasInteracted.value && control.value.errors);
|
|
17
120
|
|
|
18
121
|
function onInput(val: string | undefined) {
|
|
19
|
-
|
|
20
|
-
|
|
122
|
+
// Convert empty strings to undefined for proper required field validation
|
|
123
|
+
const newValue = val && val.trim() !== "" ? val : undefined;
|
|
124
|
+
if (control.value.data !== newValue) {
|
|
21
125
|
handleChange(control.value.path, newValue);
|
|
22
126
|
}
|
|
23
127
|
}
|
|
128
|
+
|
|
129
|
+
function onBlur() {
|
|
130
|
+
if (hasFocused.value) {
|
|
131
|
+
hasInteracted.value = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function onFocus() {
|
|
136
|
+
hasFocused.value = true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Autocomplete specific handlers
|
|
140
|
+
const onComplete = (event: { query: string }) => {
|
|
141
|
+
query.value = event.query;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
145
|
+
const newValue = (event.value as { value?: unknown })?.value ?? event.value;
|
|
146
|
+
handleChange(control.value.path, newValue);
|
|
147
|
+
};
|
|
24
148
|
</script>
|
|
25
149
|
|
|
26
150
|
<template>
|
|
@@ -31,17 +155,36 @@ function onInput(val: string | undefined) {
|
|
|
31
155
|
<div v-if="control.description" class="text-color-secondary text-left">
|
|
32
156
|
{{ control.description }}
|
|
33
157
|
</div>
|
|
158
|
+
<AutoComplete
|
|
159
|
+
v-if="isAutocomplete"
|
|
160
|
+
class="w-full"
|
|
161
|
+
:model-value="control.data ?? ''"
|
|
162
|
+
:suggestions="items"
|
|
163
|
+
option-label="label"
|
|
164
|
+
:placeholder="placeholder"
|
|
165
|
+
:disabled="!control.enabled"
|
|
166
|
+
:aria-invalid="!!showErrors || undefined"
|
|
167
|
+
@complete="onComplete"
|
|
168
|
+
@item-select="onSelect"
|
|
169
|
+
@update:model-value="onInput"
|
|
170
|
+
@blur="onBlur"
|
|
171
|
+
@focus="onFocus"
|
|
172
|
+
/>
|
|
34
173
|
<InputText
|
|
174
|
+
v-else
|
|
35
175
|
class="w-full"
|
|
36
176
|
:model-value="control.data ?? ''"
|
|
37
177
|
:disabled="!control.enabled"
|
|
38
|
-
:aria-invalid="!!
|
|
178
|
+
:aria-invalid="!!showErrors || undefined"
|
|
39
179
|
:placeholder="placeholder"
|
|
40
180
|
autocapitalize="off"
|
|
41
181
|
autocomplete="off"
|
|
42
182
|
spellcheck="false"
|
|
43
183
|
@update:model-value="onInput"
|
|
184
|
+
@blur="onBlur"
|
|
185
|
+
@focus="onFocus"
|
|
44
186
|
/>
|
|
45
|
-
<small v-if="
|
|
187
|
+
<small v-if="error" class="p-error" role="alert">Failed: {{ error }}</small>
|
|
188
|
+
<small v-else-if="showErrors" class="p-error">{{ control.errors }}</small>
|
|
46
189
|
</div>
|
|
47
190
|
</template>
|