@narrative.io/jsonforms-provider-protocols 3.0.0-beta.14 → 3.0.0-beta.17
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/initFormData.js +1 -24
- package/dist/core/initFormData.js.map +1 -1
- package/dist/core/projection.d.ts +4 -0
- package/dist/core/projection.d.ts.map +1 -1
- package/dist/core/projection.js +4 -1
- package/dist/core/projection.js.map +1 -1
- package/dist/core/refs.d.ts +42 -0
- package/dist/core/refs.d.ts.map +1 -0
- package/dist/core/refs.js +53 -0
- package/dist/core/refs.js.map +1 -0
- package/dist/core/resolveScope.d.ts +6 -0
- package/dist/core/resolveScope.d.ts.map +1 -1
- package/dist/core/resolveScope.js +4 -2
- package/dist/core/resolveScope.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 +6 -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 +12 -7
- 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 +12 -7
- package/dist/vue/components/ProviderSelect.vue2.js.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfEnum.vue.js +6 -1
- 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 +6 -1
- 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 +2 -1
- 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 +10 -2
- 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 +2 -1
- package/dist/vue/primevue/JfTextArea.vue.js.map +1 -1
- package/dist/vue/utils/placeholder.d.ts +17 -0
- package/dist/vue/utils/placeholder.d.ts.map +1 -0
- package/dist/vue/utils/placeholder.js +17 -0
- package/dist/vue/utils/placeholder.js.map +1 -0
- package/package.json +1 -1
- package/src/core/initFormData.ts +1 -43
- package/src/core/projection.ts +10 -2
- package/src/core/refs.ts +114 -0
- package/src/core/resolveScope.ts +11 -2
- package/src/vue/components/ProviderAutocomplete.vue +6 -7
- package/src/vue/components/ProviderMultiSelect.vue +12 -12
- package/src/vue/components/ProviderSelect.vue +12 -12
- package/src/vue/primevue/JfEnum.vue +5 -3
- package/src/vue/primevue/JfEnumArray.vue +5 -3
- package/src/vue/primevue/JfNumber.vue +3 -2
- package/src/vue/primevue/JfText.vue +13 -5
- package/src/vue/primevue/JfTextArea.vue +3 -4
- package/src/vue/utils/placeholder.ts +42 -0
package/src/core/resolveScope.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { deepDeref, deref } from "./refs";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Resolve a JSON Forms scope path to its schema within a root schema.
|
|
3
5
|
* Handles nested paths like "#/properties/parent/properties/child".
|
|
@@ -6,6 +8,12 @@
|
|
|
6
8
|
* - "properties" segments navigate into object `.properties`
|
|
7
9
|
* - "items" segments navigate into array `.items`
|
|
8
10
|
* - all other segments index directly into the current object
|
|
11
|
+
*
|
|
12
|
+
* `$ref` nodes are dereferenced transparently at every step, so scopes that
|
|
13
|
+
* cross a `$ref` boundary (e.g. `items: { $ref: "#/$defs/X" }`) resolve to
|
|
14
|
+
* the target schema rather than returning `{}`. The returned schema is also
|
|
15
|
+
* deep-dereferenced so downstream walkers (e.g. `getProjectedSchema`) can
|
|
16
|
+
* operate on a self-contained sub-schema without needing the original root.
|
|
9
17
|
*/
|
|
10
18
|
export function resolveScopeSchema(
|
|
11
19
|
scope: string,
|
|
@@ -17,13 +25,14 @@ export function resolveScopeSchema(
|
|
|
17
25
|
|
|
18
26
|
// Remove the leading "#/" and split into segments
|
|
19
27
|
const path = scope.replace(/^#\/?/, "");
|
|
20
|
-
if (!path) return rootSchema;
|
|
28
|
+
if (!path) return deepDeref(rootSchema, rootSchema);
|
|
21
29
|
|
|
22
30
|
const segments = path.split("/");
|
|
23
31
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
32
|
let current: any = rootSchema;
|
|
25
33
|
|
|
26
34
|
for (const segment of segments) {
|
|
35
|
+
current = deref(current, rootSchema);
|
|
27
36
|
if (!current || typeof current !== "object") return undefined;
|
|
28
37
|
|
|
29
38
|
if (segment === "properties") {
|
|
@@ -35,5 +44,5 @@ export function resolveScopeSchema(
|
|
|
35
44
|
}
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
return current;
|
|
47
|
+
return deepDeref(current, rootSchema);
|
|
39
48
|
}
|
|
@@ -12,10 +12,11 @@ const props = defineProps<{
|
|
|
12
12
|
path: string;
|
|
13
13
|
}>();
|
|
14
14
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
15
|
-
const {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const {
|
|
16
|
+
projectedData,
|
|
17
|
+
projectedLabel,
|
|
18
|
+
handleProjectedChange: handleChange,
|
|
19
|
+
} = useProjection(control, rawHandleChange);
|
|
19
20
|
|
|
20
21
|
const binding = computed(() => {
|
|
21
22
|
const provider = control.value.uischema?.options?.provider;
|
|
@@ -57,9 +58,7 @@ const onSelect = (event: { value?: { value?: unknown } | unknown }) => {
|
|
|
57
58
|
|
|
58
59
|
<template>
|
|
59
60
|
<div class="jf-control">
|
|
60
|
-
<label v-if="
|
|
61
|
-
control.schema.title
|
|
62
|
-
}}</label>
|
|
61
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
63
62
|
<div v-if="control.description" class="jf-description">
|
|
64
63
|
{{ control.description }}
|
|
65
64
|
</div>
|
|
@@ -5,6 +5,7 @@ import { computed, inject, watch } from "vue";
|
|
|
5
5
|
import { useProvider } from "../composables/useProvider";
|
|
6
6
|
import { useProjection } from "../composables/useProjection";
|
|
7
7
|
import { shouldAutoSelectMulti } from "../utils/autoSelect";
|
|
8
|
+
import { resolvePlaceholder } from "../utils/placeholder";
|
|
8
9
|
import MultiSelect from "primevue/multiselect";
|
|
9
10
|
|
|
10
11
|
const props = defineProps<{
|
|
@@ -13,10 +14,11 @@ const props = defineProps<{
|
|
|
13
14
|
path: string;
|
|
14
15
|
}>();
|
|
15
16
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
16
|
-
const {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const {
|
|
18
|
+
projectedData,
|
|
19
|
+
projectedLabel,
|
|
20
|
+
handleProjectedChange: handleChange,
|
|
21
|
+
} = useProjection(control, rawHandleChange);
|
|
20
22
|
|
|
21
23
|
const binding = computed(() => {
|
|
22
24
|
const provider = control.value.uischema?.options?.provider;
|
|
@@ -94,19 +96,17 @@ const value = computed({
|
|
|
94
96
|
|
|
95
97
|
const placeholder = computed(() => {
|
|
96
98
|
if (loading.value) return "Loading…";
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
return resolvePlaceholder(
|
|
100
|
+
control.value.uischema,
|
|
101
|
+
projectedLabel.value,
|
|
102
|
+
"select",
|
|
103
|
+
);
|
|
102
104
|
});
|
|
103
105
|
</script>
|
|
104
106
|
|
|
105
107
|
<template>
|
|
106
108
|
<div class="jf-control">
|
|
107
|
-
<label v-if="
|
|
108
|
-
control.schema.title
|
|
109
|
-
}}</label>
|
|
109
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
110
110
|
<div v-if="control.description" class="jf-description">
|
|
111
111
|
{{ control.description }}
|
|
112
112
|
</div>
|
|
@@ -6,6 +6,7 @@ import { useProvider } from "../composables/useProvider";
|
|
|
6
6
|
import { useDeriveInitialValue } from "../composables/useDeriveInitialValue";
|
|
7
7
|
import { useProjection } from "../composables/useProjection";
|
|
8
8
|
import { shouldAutoSelect } from "../utils/autoSelect";
|
|
9
|
+
import { resolvePlaceholder } from "../utils/placeholder";
|
|
9
10
|
import Dropdown from "primevue/dropdown";
|
|
10
11
|
|
|
11
12
|
const props = defineProps<{
|
|
@@ -14,10 +15,11 @@ const props = defineProps<{
|
|
|
14
15
|
path: string;
|
|
15
16
|
}>();
|
|
16
17
|
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const {
|
|
19
|
+
projectedData,
|
|
20
|
+
projectedLabel,
|
|
21
|
+
handleProjectedChange: handleChange,
|
|
22
|
+
} = useProjection(control, rawHandleChange);
|
|
21
23
|
|
|
22
24
|
const binding = computed(() => {
|
|
23
25
|
const provider = control.value.uischema?.options?.provider;
|
|
@@ -79,19 +81,17 @@ const value = computed({
|
|
|
79
81
|
|
|
80
82
|
const placeholder = computed(() => {
|
|
81
83
|
if (loading.value) return "Loading…";
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
return resolvePlaceholder(
|
|
85
|
+
control.value.uischema,
|
|
86
|
+
projectedLabel.value,
|
|
87
|
+
"select",
|
|
88
|
+
);
|
|
87
89
|
});
|
|
88
90
|
</script>
|
|
89
91
|
|
|
90
92
|
<template>
|
|
91
93
|
<div class="jf-control">
|
|
92
|
-
<label v-if="
|
|
93
|
-
control.schema.title
|
|
94
|
-
}}</label>
|
|
94
|
+
<label v-if="projectedLabel" class="jf-label">{{ projectedLabel }}</label>
|
|
95
95
|
<div v-if="control.description" class="jf-description">
|
|
96
96
|
{{ control.description }}
|
|
97
97
|
</div>
|
|
@@ -49,6 +49,7 @@ import { useDeriveInitialValue } from "../composables/useDeriveInitialValue";
|
|
|
49
49
|
import { useProjection } from "../composables/useProjection";
|
|
50
50
|
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
51
51
|
import { shouldAutoSelect } from "../utils/autoSelect";
|
|
52
|
+
import { resolvePlaceholder } from "../utils/placeholder";
|
|
52
53
|
import Dropdown from "primevue/dropdown";
|
|
53
54
|
|
|
54
55
|
// Access props from the component instance
|
|
@@ -131,9 +132,10 @@ const {
|
|
|
131
132
|
|
|
132
133
|
const placeholder = computed<string | undefined>(() => {
|
|
133
134
|
if (loading.value) return "Loading…";
|
|
134
|
-
return (
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
return resolvePlaceholder(
|
|
136
|
+
control.value.uischema,
|
|
137
|
+
projectedLabel.value,
|
|
138
|
+
"select",
|
|
137
139
|
);
|
|
138
140
|
});
|
|
139
141
|
|
|
@@ -47,6 +47,7 @@ import { useDerive } from "../composables/useDerive";
|
|
|
47
47
|
import { useProjection } from "../composables/useProjection";
|
|
48
48
|
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
49
49
|
import { shouldAutoSelectMulti } from "../utils/autoSelect";
|
|
50
|
+
import { resolvePlaceholder } from "../utils/placeholder";
|
|
50
51
|
import MultiSelect from "primevue/multiselect";
|
|
51
52
|
|
|
52
53
|
// Access props from the component instance
|
|
@@ -164,9 +165,10 @@ watch(
|
|
|
164
165
|
|
|
165
166
|
const placeholder = computed<string | undefined>(() => {
|
|
166
167
|
if (loading.value) return "Loading…";
|
|
167
|
-
return (
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
return resolvePlaceholder(
|
|
169
|
+
control.value.uischema,
|
|
170
|
+
projectedLabel.value,
|
|
171
|
+
"select",
|
|
170
172
|
);
|
|
171
173
|
});
|
|
172
174
|
|
|
@@ -45,6 +45,7 @@ import { useDerive } from "../composables/useDerive";
|
|
|
45
45
|
import { useDeriveInitialValue } from "../composables/useDeriveInitialValue";
|
|
46
46
|
import { useProjection } from "../composables/useProjection";
|
|
47
47
|
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
48
|
+
import { resolvePlaceholder } from "../utils/placeholder";
|
|
48
49
|
import InputNumber from "primevue/inputnumber";
|
|
49
50
|
|
|
50
51
|
// Access props from the component instance
|
|
@@ -64,8 +65,8 @@ const options = computed(
|
|
|
64
65
|
?.options ?? {},
|
|
65
66
|
);
|
|
66
67
|
|
|
67
|
-
const placeholder = computed<string | undefined>(
|
|
68
|
-
(
|
|
68
|
+
const placeholder = computed<string | undefined>(() =>
|
|
69
|
+
resolvePlaceholder(control.value.uischema, projectedLabel.value, "input"),
|
|
69
70
|
);
|
|
70
71
|
|
|
71
72
|
// Add derive functionality
|
|
@@ -46,6 +46,7 @@ import { useDerive } from "../composables/useDerive";
|
|
|
46
46
|
import { useDeriveInitialValue } from "../composables/useDeriveInitialValue";
|
|
47
47
|
import { useProjection } from "../composables/useProjection";
|
|
48
48
|
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
49
|
+
import { resolvePlaceholder } from "../utils/placeholder";
|
|
49
50
|
import InputText from "primevue/inputtext";
|
|
50
51
|
import AutoComplete from "primevue/autocomplete";
|
|
51
52
|
|
|
@@ -111,16 +112,23 @@ watch(query, () => {
|
|
|
111
112
|
if (binding.value?.load === "query") reload();
|
|
112
113
|
});
|
|
113
114
|
|
|
115
|
+
const isAutocomplete = computed(() => !!binding.value);
|
|
116
|
+
|
|
114
117
|
const placeholder = computed<string | undefined>(() => {
|
|
115
118
|
if (loading.value) return "Loading…";
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
const explicit = (
|
|
120
|
+
control.value.uischema as { options?: { placeholder?: string } }
|
|
121
|
+
)?.options?.placeholder;
|
|
122
|
+
if (explicit) return explicit;
|
|
123
|
+
// In autocomplete mode the input is really a search box — use matching UX.
|
|
124
|
+
if (isAutocomplete.value) return "Type to search…";
|
|
125
|
+
return resolvePlaceholder(
|
|
126
|
+
control.value.uischema,
|
|
127
|
+
projectedLabel.value,
|
|
128
|
+
"input",
|
|
119
129
|
);
|
|
120
130
|
});
|
|
121
131
|
|
|
122
|
-
const isAutocomplete = computed(() => !!binding.value);
|
|
123
|
-
|
|
124
132
|
// Add derive functionality
|
|
125
133
|
useDerive({ control, handleChange, data: projectedData });
|
|
126
134
|
|
|
@@ -43,6 +43,7 @@ import { useJsonFormsControl } from "@jsonforms/vue";
|
|
|
43
43
|
import { computed, getCurrentInstance } from "vue";
|
|
44
44
|
import { useProjection } from "../composables/useProjection";
|
|
45
45
|
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
46
|
+
import { resolvePlaceholder } from "../utils/placeholder";
|
|
46
47
|
import Textarea from "primevue/textarea";
|
|
47
48
|
|
|
48
49
|
// Access props from the component instance
|
|
@@ -56,10 +57,8 @@ const {
|
|
|
56
57
|
projectedLabel,
|
|
57
58
|
} = useProjection(control, rawHandleChange);
|
|
58
59
|
|
|
59
|
-
const placeholder = computed<string | undefined>(
|
|
60
|
-
()
|
|
61
|
-
(control.value.uischema as { options?: { placeholder?: string } })?.options
|
|
62
|
-
?.placeholder ?? control.value.description,
|
|
60
|
+
const placeholder = computed<string | undefined>(() =>
|
|
61
|
+
resolvePlaceholder(control.value.uischema, projectedLabel.value, "input"),
|
|
63
62
|
);
|
|
64
63
|
|
|
65
64
|
// Track user interaction — errors only show after blur
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placeholder resolution for form controls.
|
|
3
|
+
*
|
|
4
|
+
* Precedence:
|
|
5
|
+
* 1. `uischema.options.placeholder` (explicit author intent — always wins)
|
|
6
|
+
* 2. `Select ${label}` or `Enter ${label}` when a label is resolvable
|
|
7
|
+
* 3. Kind-appropriate bare fallback
|
|
8
|
+
*
|
|
9
|
+
* Never falls back to `schema.description` — descriptions are rendered as
|
|
10
|
+
* prose above the field by our renderers, so re-using them as placeholder
|
|
11
|
+
* produces a duplicated, truncated string inside the input.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type PlaceholderKind = "select" | "input";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Strip a trailing required-indicator asterisk (" *" or "*") that `resolveLabel`
|
|
18
|
+
* appends for required fields, so composed placeholders read naturally.
|
|
19
|
+
*/
|
|
20
|
+
function stripRequiredMarker(label: string): string {
|
|
21
|
+
return label.replace(/\s*\*\s*$/, "").trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolvePlaceholder(
|
|
25
|
+
uischema: { options?: unknown } | undefined,
|
|
26
|
+
resolvedLabel: string | undefined,
|
|
27
|
+
kind: PlaceholderKind,
|
|
28
|
+
): string | undefined {
|
|
29
|
+
const options = uischema?.options;
|
|
30
|
+
const explicit =
|
|
31
|
+
options && typeof options === "object"
|
|
32
|
+
? (options as Record<string, unknown>).placeholder
|
|
33
|
+
: undefined;
|
|
34
|
+
if (typeof explicit === "string" && explicit.length > 0) return explicit;
|
|
35
|
+
|
|
36
|
+
const label = resolvedLabel ? stripRequiredMarker(resolvedLabel) : "";
|
|
37
|
+
if (label) {
|
|
38
|
+
return kind === "select" ? `Select ${label}` : `Enter ${label}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return kind === "select" ? "Select…" : undefined;
|
|
42
|
+
}
|