@narrative.io/jsonforms-provider-protocols 3.0.0-beta.5 → 3.0.0-beta.7
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.d.ts +10 -0
- package/dist/core/initFormData.d.ts.map +1 -0
- package/dist/core/initFormData.js +99 -0
- package/dist/core/initFormData.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/index.js +18 -8
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.d.ts.map +1 -1
- package/dist/vue/primevue/JfNumber.vue.js +2 -0
- package/dist/vue/primevue/JfNumber.vue.js.map +1 -1
- package/package.json +1 -1
- package/src/core/initFormData.ts +189 -0
- package/src/index.ts +1 -0
- package/src/vue/index.ts +16 -5
- package/src/vue/primevue/JfNumber.vue +4 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize a form data object from a JSON Schema.
|
|
3
|
+
* Resolves $ref, const, default, oneOf/discriminator, and typed empty values.
|
|
4
|
+
*
|
|
5
|
+
* @param schema - The full JSON Schema (must include $defs if $refs are used)
|
|
6
|
+
* @param seed - Optional existing data to merge (seed values take priority)
|
|
7
|
+
* @returns A data object with all schema-defined fields initialized
|
|
8
|
+
*/
|
|
9
|
+
export declare function initFormDataFromSchema(schema: Record<string, unknown>, seed?: Record<string, unknown>): Record<string, unknown>;
|
|
10
|
+
//# sourceMappingURL=initFormData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initFormData.d.ts","sourceRoot":"","sources":["../../src/core/initFormData.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyBzB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
function initFormDataFromSchema(schema, seed) {
|
|
2
|
+
const result = initProperty(schema, schema, seed);
|
|
3
|
+
if (result && typeof result === "object" && !Array.isArray(result) && seed && typeof seed === "object") {
|
|
4
|
+
const schemaKeys = new Set(
|
|
5
|
+
Object.keys(
|
|
6
|
+
resolveRef(schema, schema)?.properties ?? {}
|
|
7
|
+
)
|
|
8
|
+
);
|
|
9
|
+
for (const key of Object.keys(seed)) {
|
|
10
|
+
if (!schemaKeys.has(key) && !(key in result)) {
|
|
11
|
+
result[key] = seed[key];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return result ?? {};
|
|
16
|
+
}
|
|
17
|
+
function resolveRef(property, root, seen) {
|
|
18
|
+
if (!property || typeof property !== "object") return property;
|
|
19
|
+
const ref = property.$ref;
|
|
20
|
+
if (!ref) return property;
|
|
21
|
+
const visited = seen ?? /* @__PURE__ */ new Set();
|
|
22
|
+
if (visited.has(ref)) return property;
|
|
23
|
+
visited.add(ref);
|
|
24
|
+
const resolved = resolvePointer(root, ref);
|
|
25
|
+
if (!resolved) return property;
|
|
26
|
+
return resolveRef(resolved, root, visited);
|
|
27
|
+
}
|
|
28
|
+
function resolvePointer(obj, pointer) {
|
|
29
|
+
if (!pointer.startsWith("#/")) return void 0;
|
|
30
|
+
const parts = pointer.slice(2).split("/");
|
|
31
|
+
let current = obj;
|
|
32
|
+
for (const part of parts) {
|
|
33
|
+
if (current && typeof current === "object" && part in current) {
|
|
34
|
+
current = current[part];
|
|
35
|
+
} else {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return current;
|
|
40
|
+
}
|
|
41
|
+
function initProperty(property, root, seed) {
|
|
42
|
+
if (!property || typeof property !== "object") return null;
|
|
43
|
+
const resolved = resolveRef(property, root);
|
|
44
|
+
if (Array.isArray(resolved.oneOf)) {
|
|
45
|
+
return initOneOf(resolved, root, seed);
|
|
46
|
+
}
|
|
47
|
+
const type = resolved.type;
|
|
48
|
+
if (seed !== void 0 && seed !== null && type !== "object") {
|
|
49
|
+
return seed;
|
|
50
|
+
}
|
|
51
|
+
if ("const" in resolved) {
|
|
52
|
+
return resolved.const;
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(resolved.enum) && resolved.enum.length === 1) {
|
|
55
|
+
return resolved.enum[0];
|
|
56
|
+
}
|
|
57
|
+
if ("default" in resolved) {
|
|
58
|
+
return resolved.default;
|
|
59
|
+
}
|
|
60
|
+
switch (type) {
|
|
61
|
+
case "object":
|
|
62
|
+
return initObject(resolved, root, seed);
|
|
63
|
+
case "array":
|
|
64
|
+
return seed !== void 0 && seed !== null ? seed : [];
|
|
65
|
+
case "string":
|
|
66
|
+
return "";
|
|
67
|
+
case "boolean":
|
|
68
|
+
return false;
|
|
69
|
+
case "number":
|
|
70
|
+
case "integer":
|
|
71
|
+
return null;
|
|
72
|
+
default:
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function initObject(schema, root, seed) {
|
|
77
|
+
const properties = schema.properties;
|
|
78
|
+
if (!properties) {
|
|
79
|
+
return seed && typeof seed === "object" ? { ...seed } : {};
|
|
80
|
+
}
|
|
81
|
+
const result = {};
|
|
82
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
83
|
+
const seedValue = seed && typeof seed === "object" ? seed[key] : void 0;
|
|
84
|
+
result[key] = initProperty(propSchema, root, seedValue);
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
function initOneOf(schema, root, seed) {
|
|
89
|
+
const variants = schema.oneOf;
|
|
90
|
+
if (!variants || variants.length === 0) return null;
|
|
91
|
+
const first = variants[0];
|
|
92
|
+
if (!first) return null;
|
|
93
|
+
const firstVariant = resolveRef(first, root);
|
|
94
|
+
return initProperty(firstVariant, root, seed);
|
|
95
|
+
}
|
|
96
|
+
export {
|
|
97
|
+
initFormDataFromSchema
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=initFormData.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initFormData.js","sources":["../../src/core/initFormData.ts"],"sourcesContent":["/**\n * Initialize a form data object from a JSON Schema.\n * Resolves $ref, const, default, oneOf/discriminator, and typed empty values.\n *\n * @param schema - The full JSON Schema (must include $defs if $refs are used)\n * @param seed - Optional existing data to merge (seed values take priority)\n * @returns A data object with all schema-defined fields initialized\n */\nexport function initFormDataFromSchema(\n schema: Record<string, unknown>,\n seed?: Record<string, unknown>,\n): Record<string, unknown> {\n const result = initProperty(schema, schema, seed) as Record<string, unknown>;\n\n // If result is an object and seed has extra keys not in schema, preserve them\n if (\n result &&\n typeof result === \"object\" &&\n !Array.isArray(result) &&\n seed &&\n typeof seed === \"object\"\n ) {\n const schemaKeys = new Set(\n Object.keys(\n (resolveRef(schema, schema) as Record<string, unknown>)?.properties ??\n {},\n ),\n );\n for (const key of Object.keys(seed)) {\n if (!schemaKeys.has(key) && !(key in result)) {\n result[key] = seed[key];\n }\n }\n }\n\n return result ?? {};\n}\n\n/**\n * Resolve a $ref pointer against the root schema's $defs.\n * Supports nested $ref chains.\n */\nfunction resolveRef(\n property: Record<string, unknown>,\n root: Record<string, unknown>,\n seen?: Set<string>,\n): Record<string, unknown> {\n if (!property || typeof property !== \"object\") return property;\n\n const ref = property.$ref as string | undefined;\n if (!ref) return property;\n\n // Guard against circular refs\n const visited = seen ?? new Set<string>();\n if (visited.has(ref)) return property;\n visited.add(ref);\n\n const resolved = resolvePointer(root, ref);\n if (!resolved) return property;\n\n // Continue resolving if the result itself has a $ref\n return resolveRef(resolved as Record<string, unknown>, root, visited);\n}\n\n/**\n * Resolve a JSON pointer like \"#/$defs/Price\" against an object.\n */\nfunction resolvePointer(\n obj: Record<string, unknown>,\n pointer: string,\n): unknown {\n if (!pointer.startsWith(\"#/\")) return undefined;\n const parts = pointer.slice(2).split(\"/\");\n let current: unknown = obj;\n for (const part of parts) {\n if (current && typeof current === \"object\" && part in current) {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n return current;\n}\n\n/**\n * Initialize a single property value based on its schema definition.\n */\nfunction initProperty(\n property: Record<string, unknown>,\n root: Record<string, unknown>,\n seed?: unknown,\n): unknown {\n if (!property || typeof property !== \"object\") return null;\n\n // Resolve $ref first\n const resolved = resolveRef(property, root);\n\n // Handle oneOf with discriminator — pick first variant\n if (Array.isArray(resolved.oneOf)) {\n return initOneOf(resolved, root, seed);\n }\n\n // Priority 1: seed wins (for object types, we merge recursively below)\n // For non-object types, return seed directly if present\n const type = resolved.type as string | undefined;\n if (seed !== undefined && seed !== null && type !== \"object\") {\n return seed;\n }\n\n // Priority 2: const\n if (\"const\" in resolved) {\n return resolved.const;\n }\n\n // Priority 3: single-value enum\n if (\n Array.isArray(resolved.enum) &&\n (resolved.enum as unknown[]).length === 1\n ) {\n return (resolved.enum as unknown[])[0];\n }\n\n // Priority 4: default\n if (\"default\" in resolved) {\n return resolved.default;\n }\n\n // Priority 5: typed empty values\n switch (type) {\n case \"object\":\n return initObject(resolved, root, seed as Record<string, unknown>);\n case \"array\":\n return seed !== undefined && seed !== null ? seed : [];\n case \"string\":\n return \"\";\n case \"boolean\":\n return false;\n case \"number\":\n case \"integer\":\n return null;\n default:\n return null;\n }\n}\n\n/**\n * Initialize an object type by recursing into its properties.\n */\nfunction initObject(\n schema: Record<string, unknown>,\n root: Record<string, unknown>,\n seed?: Record<string, unknown>,\n): Record<string, unknown> {\n const properties = schema.properties as\n | Record<string, Record<string, unknown>>\n | undefined;\n if (!properties) {\n // Object with no defined properties — return seed or empty object\n return seed && typeof seed === \"object\" ? { ...seed } : {};\n }\n\n const result: Record<string, unknown> = {};\n\n for (const [key, propSchema] of Object.entries(properties)) {\n const seedValue = seed && typeof seed === \"object\" ? seed[key] : undefined;\n result[key] = initProperty(propSchema, root, seedValue);\n }\n\n return result;\n}\n\n/**\n * Handle oneOf schemas — pick the first variant and initialize it.\n */\nfunction initOneOf(\n schema: Record<string, unknown>,\n root: Record<string, unknown>,\n seed?: unknown,\n): unknown {\n const variants = schema.oneOf as Record<string, unknown>[];\n if (!variants || variants.length === 0) return null;\n\n const first = variants[0];\n if (!first) return null;\n\n // Pick first variant, resolve its $ref if needed\n const firstVariant = resolveRef(first, root);\n return initProperty(firstVariant, root, seed);\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./core/transforms";
|
|
|
8
8
|
export * from "./core/projection";
|
|
9
9
|
export * from "./core/resolveScope";
|
|
10
10
|
export * from "./core/types";
|
|
11
|
+
export { initFormDataFromSchema } from "./core/initFormData";
|
|
11
12
|
export { RestApiProtocol } from "./protocols/rest_api";
|
|
12
13
|
export { providerRenderers, primevueRenderers, ProviderAutocomplete, ProviderSelect, ProviderMultiSelect, useProvider, createDataLayer, useDataLayer, JfText, JfTextArea, JfNumber, JfEnum, JfEnumArray, JfBoolean, } from "./vue";
|
|
13
14
|
export type { DataLayer } from "./vue";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG/B,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAIzD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG/B,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAIzD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AAEpC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,WAAW,EACX,eAAe,EACf,YAAY,EACZ,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,EACN,WAAW,EACX,SAAS,GACV,MAAM,OAAO,CAAC;AACf,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;;iBAGc,GAAG,SAAS,cAAc;;AADzC,wBAYE"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { renderObj, renderTpl } from "./core/templating.js";
|
|
|
5
5
|
import { applyTransformPipeline } from "./core/transforms.js";
|
|
6
6
|
import { getProjectedSchema, getProjectedValue, parseProjectionPath, setProjectedValue } from "./core/projection.js";
|
|
7
7
|
import { resolveScopeSchema } from "./core/resolveScope.js";
|
|
8
|
+
import { initFormDataFromSchema } from "./core/initFormData.js";
|
|
8
9
|
import { RestApiProtocol } from "./protocols/rest_api.js";
|
|
9
10
|
import { providerRenderers } from "./vue/index.js";
|
|
10
11
|
import { default as default2 } from "./vue/components/ProviderAutocomplete.vue.js";
|
|
@@ -50,6 +51,7 @@ export {
|
|
|
50
51
|
index as default,
|
|
51
52
|
getProjectedSchema,
|
|
52
53
|
getProjectedValue,
|
|
54
|
+
initFormDataFromSchema,
|
|
53
55
|
jp,
|
|
54
56
|
parseProjectionPath,
|
|
55
57
|
primevueRenderers,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import type { App } from \"vue\";\nimport { cache as globalCache } from \"./core/cache\";\nimport { registry as globalRegistry } from \"./core/registry\";\nimport type { Protocol, AuthConfig } from \"./core/types\";\n\nconsole.log(\"[jsonforms-provider-protocols] loaded v2.10.0 (symlink)\");\n\nexport { cache } from \"./core/cache\";\nexport * from \"./core/jsonpath\";\nexport { registry } from \"./core/registry\";\nexport * from \"./core/templating\";\nexport * from \"./core/transforms\";\nexport * from \"./core/projection\";\nexport * from \"./core/resolveScope\";\n// Core exports\nexport * from \"./core/types\";\n\n// Protocol exports\nexport { RestApiProtocol } from \"./protocols/rest_api\";\n\n// Vue exports - using named imports to avoid potential bundling issues\nexport {\n providerRenderers,\n primevueRenderers,\n ProviderAutocomplete,\n ProviderSelect,\n ProviderMultiSelect,\n useProvider,\n createDataLayer,\n useDataLayer,\n JfText,\n JfTextArea,\n JfNumber,\n JfEnum,\n JfEnumArray,\n JfBoolean,\n} from \"./vue\";\nexport type { DataLayer } from \"./vue\";\n\nexport interface ProviderConfig {\n protocols?: Protocol[];\n auth?: AuthConfig;\n}\n\nexport default {\n install(app: App, opts?: ProviderConfig) {\n const reg = globalRegistry;\n if (opts?.protocols) {\n for (const p of opts.protocols) {\n reg.register(p);\n }\n }\n app.provide(\"providerRegistry\", reg);\n app.provide(\"providerCache\", globalCache);\n app.provide(\"providerAuth\", opts?.auth ?? {});\n },\n};\n"],"names":["globalRegistry","globalCache"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import type { App } from \"vue\";\nimport { cache as globalCache } from \"./core/cache\";\nimport { registry as globalRegistry } from \"./core/registry\";\nimport type { Protocol, AuthConfig } from \"./core/types\";\n\nconsole.log(\"[jsonforms-provider-protocols] loaded v2.10.0 (symlink)\");\n\nexport { cache } from \"./core/cache\";\nexport * from \"./core/jsonpath\";\nexport { registry } from \"./core/registry\";\nexport * from \"./core/templating\";\nexport * from \"./core/transforms\";\nexport * from \"./core/projection\";\nexport * from \"./core/resolveScope\";\n// Core exports\nexport * from \"./core/types\";\nexport { initFormDataFromSchema } from \"./core/initFormData\";\n\n// Protocol exports\nexport { RestApiProtocol } from \"./protocols/rest_api\";\n\n// Vue exports - using named imports to avoid potential bundling issues\nexport {\n providerRenderers,\n primevueRenderers,\n ProviderAutocomplete,\n ProviderSelect,\n ProviderMultiSelect,\n useProvider,\n createDataLayer,\n useDataLayer,\n JfText,\n JfTextArea,\n JfNumber,\n JfEnum,\n JfEnumArray,\n JfBoolean,\n} from \"./vue\";\nexport type { DataLayer } from \"./vue\";\n\nexport interface ProviderConfig {\n protocols?: Protocol[];\n auth?: AuthConfig;\n}\n\nexport default {\n install(app: App, opts?: ProviderConfig) {\n const reg = globalRegistry;\n if (opts?.protocols) {\n for (const p of opts.protocols) {\n reg.register(p);\n }\n }\n app.provide(\"providerRegistry\", reg);\n app.provide(\"providerCache\", globalCache);\n app.provide(\"providerAuth\", opts?.auth ?? {});\n },\n};\n"],"names":["globalRegistry","globalCache"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAKA,QAAQ,IAAI,yDAAyD;AAwCrE,MAAA,QAAe;AAAA,EACb,QAAQ,KAAU,MAAuB;AACvC,UAAM,MAAMA;AACZ,QAAI,MAAM,WAAW;AACnB,iBAAW,KAAK,KAAK,WAAW;AAC9B,YAAI,SAAS,CAAC;AAAA,MAChB;AAAA,IACF;AACA,QAAI,QAAQ,oBAAoB,GAAG;AACnC,QAAI,QAAQ,iBAAiBC,KAAW;AACxC,QAAI,QAAQ,gBAAgB,MAAM,QAAQ,CAAA,CAAE;AAAA,EAC9C;AACF;"}
|
package/dist/vue/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vue/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vue/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAWvD,OAAO,oBAAoB,MAAM,uCAAuC,CAAC;AACzE,OAAO,cAAc,MAAM,iCAAiC,CAAC;AAC7D,OAAO,mBAAmB,MAAM,sCAAsC,CAAC;AAqEvE,eAAO,MAAM,iBAAiB;;;;;;;;;;;GAI7B,CAAC;AAGF,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAG1E,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC3E,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,YAAY,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,cAAc,WAAW,CAAC;AAG1B,OAAO,EACL,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,EACN,WAAW,EACX,SAAS,GACV,MAAM,YAAY,CAAC"}
|
package/dist/vue/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { rankWith, and, or, isStringControl, isNumberControl, isControl } from "@jsonforms/core";
|
|
1
|
+
import { rankWith, and, or, isStringControl, isNumberControl, isIntegerControl, isControl } from "@jsonforms/core";
|
|
2
2
|
import { resolveScopeSchema } from "../core/resolveScope.js";
|
|
3
3
|
import _sfc_main from "./components/ProviderAutocomplete.vue.js";
|
|
4
4
|
import ProviderSelect from "./components/ProviderSelect.vue.js";
|
|
@@ -30,19 +30,29 @@ const isIntegerScope = (uischema, schema) => {
|
|
|
30
30
|
return propertySchema?.type === "integer";
|
|
31
31
|
};
|
|
32
32
|
const providerSelectTester = rankWith(
|
|
33
|
-
|
|
34
|
-
// Higher than PrimeVue
|
|
33
|
+
107,
|
|
34
|
+
// Higher than PrimeVue JfNumber integer renderer (106) so provider wins
|
|
35
35
|
and(
|
|
36
|
-
or(
|
|
36
|
+
or(
|
|
37
|
+
isStringControl,
|
|
38
|
+
isNumberControl,
|
|
39
|
+
isIntegerControl,
|
|
40
|
+
and(isControl, isIntegerScope)
|
|
41
|
+
),
|
|
37
42
|
hasProvider,
|
|
38
43
|
(uischema) => !uischema?.options?.autocomplete
|
|
39
44
|
)
|
|
40
45
|
);
|
|
41
46
|
const providerAutocompleteTester = rankWith(
|
|
42
|
-
|
|
43
|
-
// Higher than
|
|
47
|
+
108,
|
|
48
|
+
// Higher than providerSelectTester so autocomplete variant wins when flagged
|
|
44
49
|
and(
|
|
45
|
-
or(
|
|
50
|
+
or(
|
|
51
|
+
isStringControl,
|
|
52
|
+
isNumberControl,
|
|
53
|
+
isIntegerControl,
|
|
54
|
+
and(isControl, isIntegerScope)
|
|
55
|
+
),
|
|
46
56
|
hasProvider,
|
|
47
57
|
(uischema) => uischema?.options?.autocomplete === true
|
|
48
58
|
)
|
|
@@ -60,7 +70,7 @@ const isArrayControl = (uischema, schema) => {
|
|
|
60
70
|
return propertySchema?.type === "array";
|
|
61
71
|
};
|
|
62
72
|
const providerMultiSelectTester = rankWith(
|
|
63
|
-
|
|
73
|
+
109,
|
|
64
74
|
// Highest priority for array controls with providers
|
|
65
75
|
and(isArrayControl, hasProvider)
|
|
66
76
|
);
|
package/dist/vue/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/vue/index.ts"],"sourcesContent":["import type { UISchemaElement } from \"@jsonforms/core\";\nimport {\n and,\n isNumberControl,\n isStringControl,\n or,\n rankWith,\n isControl,\n} from \"@jsonforms/core\";\nimport { resolveScopeSchema } from \"../core/resolveScope\";\nimport ProviderAutocomplete from \"./components/ProviderAutocomplete.vue\";\nimport ProviderSelect from \"./components/ProviderSelect.vue\";\nimport ProviderMultiSelect from \"./components/ProviderMultiSelect.vue\";\n\n// Custom tester that checks if provider option exists (as object or boolean)\nconst hasProvider = (uischema: UISchemaElement) => {\n return uischema?.options?.provider !== undefined;\n};\n\n// Integer fallback tester — handles nested scopes like #/properties/parent/properties/child\nconst isIntegerScope = (uischema: unknown, schema: unknown) => {\n const ui = uischema as { type?: string; scope?: string };\n if (ui?.type !== \"Control\" || !ui?.scope) return false;\n\n const propertySchema = resolveScopeSchema(\n ui.scope,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema as Record<string, any>,\n );\n return propertySchema?.type === \"integer\";\n};\n\n// Create specific testers for each component type\nconst providerSelectTester = rankWith(\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/vue/index.ts"],"sourcesContent":["import type { UISchemaElement } from \"@jsonforms/core\";\nimport {\n and,\n isIntegerControl,\n isNumberControl,\n isStringControl,\n or,\n rankWith,\n isControl,\n} from \"@jsonforms/core\";\nimport { resolveScopeSchema } from \"../core/resolveScope\";\nimport ProviderAutocomplete from \"./components/ProviderAutocomplete.vue\";\nimport ProviderSelect from \"./components/ProviderSelect.vue\";\nimport ProviderMultiSelect from \"./components/ProviderMultiSelect.vue\";\n\n// Custom tester that checks if provider option exists (as object or boolean)\nconst hasProvider = (uischema: UISchemaElement) => {\n return uischema?.options?.provider !== undefined;\n};\n\n// Integer fallback tester — handles nested scopes like #/properties/parent/properties/child\nconst isIntegerScope = (uischema: unknown, schema: unknown) => {\n const ui = uischema as { type?: string; scope?: string };\n if (ui?.type !== \"Control\" || !ui?.scope) return false;\n\n const propertySchema = resolveScopeSchema(\n ui.scope,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema as Record<string, any>,\n );\n return propertySchema?.type === \"integer\";\n};\n\n// Create specific testers for each component type\nconst providerSelectTester = rankWith(\n 107, // Higher than PrimeVue JfNumber integer renderer (106) so provider wins\n and(\n or(\n isStringControl,\n isNumberControl,\n isIntegerControl,\n and(isControl, isIntegerScope),\n ),\n hasProvider,\n (uischema) => !uischema?.options?.autocomplete,\n ),\n);\n\nconst providerAutocompleteTester = rankWith(\n 108, // Higher than providerSelectTester so autocomplete variant wins when flagged\n and(\n or(\n isStringControl,\n isNumberControl,\n isIntegerControl,\n and(isControl, isIntegerScope),\n ),\n hasProvider,\n (uischema) => uischema?.options?.autocomplete === true,\n ),\n);\n\n// Custom array tester - supports nested scope paths\nconst isArrayControl = (uischema: UISchemaElement, schema: unknown) => {\n const controlSchema = uischema as { type: string; scope?: string };\n if (controlSchema.type !== \"Control\" || !controlSchema.scope) {\n return false;\n }\n\n const propertySchema = resolveScopeSchema(\n controlSchema.scope,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema as Record<string, any>,\n );\n return propertySchema?.type === \"array\";\n};\n\nconst providerMultiSelectTester = rankWith(\n 109, // Highest priority for array controls with providers\n and(isArrayControl, hasProvider),\n);\n\nexport const providerRenderers = [\n { tester: providerMultiSelectTester, renderer: ProviderMultiSelect },\n { tester: providerAutocompleteTester, renderer: ProviderAutocomplete },\n { tester: providerSelectTester, renderer: ProviderSelect },\n];\n\n// Export PrimeVue renderers (styles are auto-injected)\nexport { primevueRenderers, registerPrimevueRenderers } from \"./primevue\";\n\n// Export individual components\nexport { ProviderAutocomplete, ProviderSelect, ProviderMultiSelect };\nexport { useProvider } from \"./composables/useProvider\";\nexport { useProjection } from \"./composables/useProjection\";\nexport type { ProjectionResult } from \"./composables/useProjection\";\nexport { createDataLayer, useDataLayer } from \"./composables/useDataLayer\";\nexport type { DataLayer } from \"./composables/useDataLayer\";\nexport { useDeriveInitialValue } from \"./composables/useDeriveInitialValue\";\nexport type { DeriveInitialValueCfg } from \"./composables/useDeriveInitialValue\";\nexport { useDirtyValidation } from \"./composables/useDirtyValidation\";\nexport * from \"./testers\";\n\n// Export individual PrimeVue components using lazy evaluation to avoid circular deps\nexport {\n JfText,\n JfTextArea,\n JfNumber,\n JfEnum,\n JfEnumArray,\n JfBoolean,\n} from \"./primevue\";\n"],"names":["ProviderAutocomplete"],"mappings":";;;;;;;;;;;;;;;;;;AAgBA,MAAM,cAAc,CAAC,aAA8B;AACjD,SAAO,UAAU,SAAS,aAAa;AACzC;AAGA,MAAM,iBAAiB,CAAC,UAAmB,WAAoB;AAC7D,QAAM,KAAK;AACX,MAAI,IAAI,SAAS,aAAa,CAAC,IAAI,MAAO,QAAO;AAEjD,QAAM,iBAAiB;AAAA,IACrB,GAAG;AAAA;AAAA,IAEH;AAAA,EAAA;AAEF,SAAO,gBAAgB,SAAS;AAClC;AAGA,MAAM,uBAAuB;AAAA,EAC3B;AAAA;AAAA,EACA;AAAA,IACE;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA,IAAI,WAAW,cAAc;AAAA,IAAA;AAAA,IAE/B;AAAA,IACA,CAAC,aAAa,CAAC,UAAU,SAAS;AAAA,EAAA;AAEtC;AAEA,MAAM,6BAA6B;AAAA,EACjC;AAAA;AAAA,EACA;AAAA,IACE;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA,IAAI,WAAW,cAAc;AAAA,IAAA;AAAA,IAE/B;AAAA,IACA,CAAC,aAAa,UAAU,SAAS,iBAAiB;AAAA,EAAA;AAEtD;AAGA,MAAM,iBAAiB,CAAC,UAA2B,WAAoB;AACrE,QAAM,gBAAgB;AACtB,MAAI,cAAc,SAAS,aAAa,CAAC,cAAc,OAAO;AAC5D,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB;AAAA,IACrB,cAAc;AAAA;AAAA,IAEd;AAAA,EAAA;AAEF,SAAO,gBAAgB,SAAS;AAClC;AAEA,MAAM,4BAA4B;AAAA,EAChC;AAAA;AAAA,EACA,IAAI,gBAAgB,WAAW;AACjC;AAEO,MAAM,oBAAoB;AAAA,EAC/B,EAAE,QAAQ,2BAA2B,UAAU,oBAAA;AAAA,EAC/C,EAAE,QAAQ,4BAA4B,UAAUA,UAAA;AAAA,EAChD,EAAE,QAAQ,sBAAsB,UAAU,eAAA;AAC5C;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JfNumber.vue.d.ts","sourceRoot":"","sources":["../../../src/vue/primevue/JfNumber.vue"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"JfNumber.vue.d.ts","sourceRoot":"","sources":["../../../src/vue/primevue/JfNumber.vue"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4JA,wBA0QK"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineComponent, getCurrentInstance, computed, createElementBlock, openBlock, createCommentVNode, createVNode, unref, toDisplayString, normalizeClass } from "vue";
|
|
2
2
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
3
3
|
import { useDerive } from "../composables/useDerive.js";
|
|
4
|
+
import { useDeriveInitialValue } from "../composables/useDeriveInitialValue.js";
|
|
4
5
|
import { useProjection } from "../composables/useProjection.js";
|
|
5
6
|
import { useDirtyValidation } from "../composables/useDirtyValidation.js";
|
|
6
7
|
import InputNumber from "primevue/inputnumber";
|
|
@@ -72,6 +73,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
72
73
|
() => options.value.placeholder ?? control.value.description
|
|
73
74
|
);
|
|
74
75
|
useDerive({ control, handleChange, data: projectedData });
|
|
76
|
+
useDeriveInitialValue({ control, handleChange });
|
|
75
77
|
const mode = computed(() => {
|
|
76
78
|
if (options.value.currency) return "currency";
|
|
77
79
|
if (options.value.decimal || typeof options.value.precision === "number")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JfNumber.vue.js","sources":["../../../src/vue/primevue/JfNumber.vue"],"sourcesContent":["<script lang=\"ts\">\nexport default {\n name: \"JfNumber\",\n props: {\n uischema: {\n type: Object,\n required: true,\n },\n schema: {\n type: Object,\n required: true,\n },\n path: {\n type: String,\n required: true,\n },\n enabled: {\n type: Boolean,\n default: undefined,\n },\n renderers: {\n type: Array,\n required: false,\n default: undefined,\n },\n cells: {\n type: Array,\n required: false,\n default: undefined,\n },\n config: {\n type: Object,\n required: false,\n default: undefined,\n },\n },\n};\n</script>\n\n<script setup lang=\"ts\">\nimport type { ControlProps } from \"@jsonforms/vue\";\nimport { useJsonFormsControl } from \"@jsonforms/vue\";\nimport { computed, getCurrentInstance } from \"vue\";\nimport { useDerive } from \"../composables/useDerive\";\nimport { useProjection } from \"../composables/useProjection\";\nimport { useDirtyValidation } from \"../composables/useDirtyValidation\";\nimport InputNumber from \"primevue/inputnumber\";\n\n// Access props from the component instance\nconst instance = getCurrentInstance()!;\nconst props = instance.props as unknown as ControlProps;\nconst { control, handleChange: rawHandleChange } = useJsonFormsControl(props);\nconst {\n projectedData,\n handleProjectedChange: handleChange,\n projectedErrors,\n projectedLabel,\n} = useProjection(control, rawHandleChange);\n\nconst options = computed(\n () =>\n (control.value.uischema as { options?: Record<string, unknown> })\n ?.options ?? {},\n);\n\nconst placeholder = computed<string | undefined>(\n () => (options.value.placeholder as string) ?? control.value.description,\n);\n\n// Add derive functionality\nuseDerive({ control, handleChange, data: projectedData });\n\n// Currency and decimal configuration\nconst mode = computed(() => {\n if (options.value.currency) return \"currency\";\n if (options.value.decimal || typeof options.value.precision === \"number\")\n return \"decimal\";\n return undefined;\n});\n\nconst currency = computed(() =>\n typeof options.value.currency === \"string\" ? options.value.currency : \"USD\",\n);\n\nconst minFractionDigits = computed(() => {\n // For integer types, no fractional digits\n if (control.value.schema?.type === \"integer\") return 0;\n if (mode.value === \"currency\") return 2;\n if (typeof options.value.precision === \"number\")\n return options.value.precision;\n return undefined;\n});\n\nconst maxFractionDigits = computed(() => {\n // For integer types, no fractional digits\n if (control.value.schema?.type === \"integer\") return 0;\n if (mode.value === \"currency\") return 2;\n if (typeof options.value.precision === \"number\")\n return options.value.precision;\n return undefined;\n});\n\nconst useGrouping = computed(() => {\n // Enable grouping for currency by default, or if explicitly set\n if (mode.value === \"currency\") return true;\n return options.value.useGrouping === true;\n});\n\n// Track user interaction — errors only show after blur\nconst { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);\n\nconst onNumber = (val: number | null) => {\n handleChange(control.value.path, val ?? undefined);\n};\n</script>\n\n<template>\n <div class=\"jf-control\">\n <label v-if=\"projectedLabel\" class=\"jf-label\">{{ projectedLabel }}</label>\n <div v-if=\"control.description\" class=\"jf-description\">\n {{ control.description }}\n </div>\n <InputNumber\n :class=\"['w-full!', { 'p-invalid': showErrors }]\"\n :input-class=\"['w-full!', { 'p-invalid': showErrors }]\"\n :use-grouping=\"useGrouping\"\n :mode=\"mode\"\n :currency=\"currency\"\n :min-fraction-digits=\"minFractionDigits\"\n :max-fraction-digits=\"maxFractionDigits\"\n :model-value=\"typeof projectedData === 'number' ? projectedData : null\"\n :placeholder=\"placeholder\"\n :disabled=\"!control.enabled\"\n :aria-invalid=\"showErrors || undefined\"\n @update:model-value=\"onNumber\"\n @blur=\"markDirty\"\n />\n <small v-if=\"showErrors\" class=\"p-error\">{{ projectedErrors }}</small>\n </div>\n</template>\n"],"names":["_openBlock","_createElementBlock","_unref","_toDisplayString","_createVNode"],"mappings":"
|
|
1
|
+
{"version":3,"file":"JfNumber.vue.js","sources":["../../../src/vue/primevue/JfNumber.vue"],"sourcesContent":["<script lang=\"ts\">\nexport default {\n name: \"JfNumber\",\n props: {\n uischema: {\n type: Object,\n required: true,\n },\n schema: {\n type: Object,\n required: true,\n },\n path: {\n type: String,\n required: true,\n },\n enabled: {\n type: Boolean,\n default: undefined,\n },\n renderers: {\n type: Array,\n required: false,\n default: undefined,\n },\n cells: {\n type: Array,\n required: false,\n default: undefined,\n },\n config: {\n type: Object,\n required: false,\n default: undefined,\n },\n },\n};\n</script>\n\n<script setup lang=\"ts\">\nimport type { ControlProps } from \"@jsonforms/vue\";\nimport { useJsonFormsControl } from \"@jsonforms/vue\";\nimport { computed, getCurrentInstance } from \"vue\";\nimport { useDerive } from \"../composables/useDerive\";\nimport { useDeriveInitialValue } from \"../composables/useDeriveInitialValue\";\nimport { useProjection } from \"../composables/useProjection\";\nimport { useDirtyValidation } from \"../composables/useDirtyValidation\";\nimport InputNumber from \"primevue/inputnumber\";\n\n// Access props from the component instance\nconst instance = getCurrentInstance()!;\nconst props = instance.props as unknown as ControlProps;\nconst { control, handleChange: rawHandleChange } = useJsonFormsControl(props);\nconst {\n projectedData,\n handleProjectedChange: handleChange,\n projectedErrors,\n projectedLabel,\n} = useProjection(control, rawHandleChange);\n\nconst options = computed(\n () =>\n (control.value.uischema as { options?: Record<string, unknown> })\n ?.options ?? {},\n);\n\nconst placeholder = computed<string | undefined>(\n () => (options.value.placeholder as string) ?? control.value.description,\n);\n\n// Add derive functionality\nuseDerive({ control, handleChange, data: projectedData });\n\n// Add deriveInitialValue — async API-based initial value seeding\nuseDeriveInitialValue({ control, handleChange });\n\n// Currency and decimal configuration\nconst mode = computed(() => {\n if (options.value.currency) return \"currency\";\n if (options.value.decimal || typeof options.value.precision === \"number\")\n return \"decimal\";\n return undefined;\n});\n\nconst currency = computed(() =>\n typeof options.value.currency === \"string\" ? options.value.currency : \"USD\",\n);\n\nconst minFractionDigits = computed(() => {\n // For integer types, no fractional digits\n if (control.value.schema?.type === \"integer\") return 0;\n if (mode.value === \"currency\") return 2;\n if (typeof options.value.precision === \"number\")\n return options.value.precision;\n return undefined;\n});\n\nconst maxFractionDigits = computed(() => {\n // For integer types, no fractional digits\n if (control.value.schema?.type === \"integer\") return 0;\n if (mode.value === \"currency\") return 2;\n if (typeof options.value.precision === \"number\")\n return options.value.precision;\n return undefined;\n});\n\nconst useGrouping = computed(() => {\n // Enable grouping for currency by default, or if explicitly set\n if (mode.value === \"currency\") return true;\n return options.value.useGrouping === true;\n});\n\n// Track user interaction — errors only show after blur\nconst { showErrors, markDirty } = useDirtyValidation(control, projectedErrors);\n\nconst onNumber = (val: number | null) => {\n handleChange(control.value.path, val ?? undefined);\n};\n</script>\n\n<template>\n <div class=\"jf-control\">\n <label v-if=\"projectedLabel\" class=\"jf-label\">{{ projectedLabel }}</label>\n <div v-if=\"control.description\" class=\"jf-description\">\n {{ control.description }}\n </div>\n <InputNumber\n :class=\"['w-full!', { 'p-invalid': showErrors }]\"\n :input-class=\"['w-full!', { 'p-invalid': showErrors }]\"\n :use-grouping=\"useGrouping\"\n :mode=\"mode\"\n :currency=\"currency\"\n :min-fraction-digits=\"minFractionDigits\"\n :max-fraction-digits=\"maxFractionDigits\"\n :model-value=\"typeof projectedData === 'number' ? projectedData : null\"\n :placeholder=\"placeholder\"\n :disabled=\"!control.enabled\"\n :aria-invalid=\"showErrors || undefined\"\n @update:model-value=\"onNumber\"\n @blur=\"markDirty\"\n />\n <small v-if=\"showErrors\" class=\"p-error\">{{ projectedErrors }}</small>\n </div>\n</template>\n"],"names":["_openBlock","_createElementBlock","_unref","_toDisplayString","_createVNode"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize a form data object from a JSON Schema.
|
|
3
|
+
* Resolves $ref, const, default, oneOf/discriminator, and typed empty values.
|
|
4
|
+
*
|
|
5
|
+
* @param schema - The full JSON Schema (must include $defs if $refs are used)
|
|
6
|
+
* @param seed - Optional existing data to merge (seed values take priority)
|
|
7
|
+
* @returns A data object with all schema-defined fields initialized
|
|
8
|
+
*/
|
|
9
|
+
export function initFormDataFromSchema(
|
|
10
|
+
schema: Record<string, unknown>,
|
|
11
|
+
seed?: Record<string, unknown>,
|
|
12
|
+
): Record<string, unknown> {
|
|
13
|
+
const result = initProperty(schema, schema, seed) as Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
// If result is an object and seed has extra keys not in schema, preserve them
|
|
16
|
+
if (
|
|
17
|
+
result &&
|
|
18
|
+
typeof result === "object" &&
|
|
19
|
+
!Array.isArray(result) &&
|
|
20
|
+
seed &&
|
|
21
|
+
typeof seed === "object"
|
|
22
|
+
) {
|
|
23
|
+
const schemaKeys = new Set(
|
|
24
|
+
Object.keys(
|
|
25
|
+
(resolveRef(schema, schema) as Record<string, unknown>)?.properties ??
|
|
26
|
+
{},
|
|
27
|
+
),
|
|
28
|
+
);
|
|
29
|
+
for (const key of Object.keys(seed)) {
|
|
30
|
+
if (!schemaKeys.has(key) && !(key in result)) {
|
|
31
|
+
result[key] = seed[key];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result ?? {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a $ref pointer against the root schema's $defs.
|
|
41
|
+
* Supports nested $ref chains.
|
|
42
|
+
*/
|
|
43
|
+
function resolveRef(
|
|
44
|
+
property: Record<string, unknown>,
|
|
45
|
+
root: Record<string, unknown>,
|
|
46
|
+
seen?: Set<string>,
|
|
47
|
+
): Record<string, unknown> {
|
|
48
|
+
if (!property || typeof property !== "object") return property;
|
|
49
|
+
|
|
50
|
+
const ref = property.$ref as string | undefined;
|
|
51
|
+
if (!ref) return property;
|
|
52
|
+
|
|
53
|
+
// Guard against circular refs
|
|
54
|
+
const visited = seen ?? new Set<string>();
|
|
55
|
+
if (visited.has(ref)) return property;
|
|
56
|
+
visited.add(ref);
|
|
57
|
+
|
|
58
|
+
const resolved = resolvePointer(root, ref);
|
|
59
|
+
if (!resolved) return property;
|
|
60
|
+
|
|
61
|
+
// Continue resolving if the result itself has a $ref
|
|
62
|
+
return resolveRef(resolved as Record<string, unknown>, root, visited);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a JSON pointer like "#/$defs/Price" against an object.
|
|
67
|
+
*/
|
|
68
|
+
function resolvePointer(
|
|
69
|
+
obj: Record<string, unknown>,
|
|
70
|
+
pointer: string,
|
|
71
|
+
): unknown {
|
|
72
|
+
if (!pointer.startsWith("#/")) return undefined;
|
|
73
|
+
const parts = pointer.slice(2).split("/");
|
|
74
|
+
let current: unknown = obj;
|
|
75
|
+
for (const part of parts) {
|
|
76
|
+
if (current && typeof current === "object" && part in current) {
|
|
77
|
+
current = (current as Record<string, unknown>)[part];
|
|
78
|
+
} else {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Initialize a single property value based on its schema definition.
|
|
87
|
+
*/
|
|
88
|
+
function initProperty(
|
|
89
|
+
property: Record<string, unknown>,
|
|
90
|
+
root: Record<string, unknown>,
|
|
91
|
+
seed?: unknown,
|
|
92
|
+
): unknown {
|
|
93
|
+
if (!property || typeof property !== "object") return null;
|
|
94
|
+
|
|
95
|
+
// Resolve $ref first
|
|
96
|
+
const resolved = resolveRef(property, root);
|
|
97
|
+
|
|
98
|
+
// Handle oneOf with discriminator — pick first variant
|
|
99
|
+
if (Array.isArray(resolved.oneOf)) {
|
|
100
|
+
return initOneOf(resolved, root, seed);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Priority 1: seed wins (for object types, we merge recursively below)
|
|
104
|
+
// For non-object types, return seed directly if present
|
|
105
|
+
const type = resolved.type as string | undefined;
|
|
106
|
+
if (seed !== undefined && seed !== null && type !== "object") {
|
|
107
|
+
return seed;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Priority 2: const
|
|
111
|
+
if ("const" in resolved) {
|
|
112
|
+
return resolved.const;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Priority 3: single-value enum
|
|
116
|
+
if (
|
|
117
|
+
Array.isArray(resolved.enum) &&
|
|
118
|
+
(resolved.enum as unknown[]).length === 1
|
|
119
|
+
) {
|
|
120
|
+
return (resolved.enum as unknown[])[0];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Priority 4: default
|
|
124
|
+
if ("default" in resolved) {
|
|
125
|
+
return resolved.default;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Priority 5: typed empty values
|
|
129
|
+
switch (type) {
|
|
130
|
+
case "object":
|
|
131
|
+
return initObject(resolved, root, seed as Record<string, unknown>);
|
|
132
|
+
case "array":
|
|
133
|
+
return seed !== undefined && seed !== null ? seed : [];
|
|
134
|
+
case "string":
|
|
135
|
+
return "";
|
|
136
|
+
case "boolean":
|
|
137
|
+
return false;
|
|
138
|
+
case "number":
|
|
139
|
+
case "integer":
|
|
140
|
+
return null;
|
|
141
|
+
default:
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Initialize an object type by recursing into its properties.
|
|
148
|
+
*/
|
|
149
|
+
function initObject(
|
|
150
|
+
schema: Record<string, unknown>,
|
|
151
|
+
root: Record<string, unknown>,
|
|
152
|
+
seed?: Record<string, unknown>,
|
|
153
|
+
): Record<string, unknown> {
|
|
154
|
+
const properties = schema.properties as
|
|
155
|
+
| Record<string, Record<string, unknown>>
|
|
156
|
+
| undefined;
|
|
157
|
+
if (!properties) {
|
|
158
|
+
// Object with no defined properties — return seed or empty object
|
|
159
|
+
return seed && typeof seed === "object" ? { ...seed } : {};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result: Record<string, unknown> = {};
|
|
163
|
+
|
|
164
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
165
|
+
const seedValue = seed && typeof seed === "object" ? seed[key] : undefined;
|
|
166
|
+
result[key] = initProperty(propSchema, root, seedValue);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Handle oneOf schemas — pick the first variant and initialize it.
|
|
174
|
+
*/
|
|
175
|
+
function initOneOf(
|
|
176
|
+
schema: Record<string, unknown>,
|
|
177
|
+
root: Record<string, unknown>,
|
|
178
|
+
seed?: unknown,
|
|
179
|
+
): unknown {
|
|
180
|
+
const variants = schema.oneOf as Record<string, unknown>[];
|
|
181
|
+
if (!variants || variants.length === 0) return null;
|
|
182
|
+
|
|
183
|
+
const first = variants[0];
|
|
184
|
+
if (!first) return null;
|
|
185
|
+
|
|
186
|
+
// Pick first variant, resolve its $ref if needed
|
|
187
|
+
const firstVariant = resolveRef(first, root);
|
|
188
|
+
return initProperty(firstVariant, root, seed);
|
|
189
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export * from "./core/projection";
|
|
|
14
14
|
export * from "./core/resolveScope";
|
|
15
15
|
// Core exports
|
|
16
16
|
export * from "./core/types";
|
|
17
|
+
export { initFormDataFromSchema } from "./core/initFormData";
|
|
17
18
|
|
|
18
19
|
// Protocol exports
|
|
19
20
|
export { RestApiProtocol } from "./protocols/rest_api";
|
package/src/vue/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { UISchemaElement } from "@jsonforms/core";
|
|
2
2
|
import {
|
|
3
3
|
and,
|
|
4
|
+
isIntegerControl,
|
|
4
5
|
isNumberControl,
|
|
5
6
|
isStringControl,
|
|
6
7
|
or,
|
|
@@ -32,18 +33,28 @@ const isIntegerScope = (uischema: unknown, schema: unknown) => {
|
|
|
32
33
|
|
|
33
34
|
// Create specific testers for each component type
|
|
34
35
|
const providerSelectTester = rankWith(
|
|
35
|
-
|
|
36
|
+
107, // Higher than PrimeVue JfNumber integer renderer (106) so provider wins
|
|
36
37
|
and(
|
|
37
|
-
or(
|
|
38
|
+
or(
|
|
39
|
+
isStringControl,
|
|
40
|
+
isNumberControl,
|
|
41
|
+
isIntegerControl,
|
|
42
|
+
and(isControl, isIntegerScope),
|
|
43
|
+
),
|
|
38
44
|
hasProvider,
|
|
39
45
|
(uischema) => !uischema?.options?.autocomplete,
|
|
40
46
|
),
|
|
41
47
|
);
|
|
42
48
|
|
|
43
49
|
const providerAutocompleteTester = rankWith(
|
|
44
|
-
|
|
50
|
+
108, // Higher than providerSelectTester so autocomplete variant wins when flagged
|
|
45
51
|
and(
|
|
46
|
-
or(
|
|
52
|
+
or(
|
|
53
|
+
isStringControl,
|
|
54
|
+
isNumberControl,
|
|
55
|
+
isIntegerControl,
|
|
56
|
+
and(isControl, isIntegerScope),
|
|
57
|
+
),
|
|
47
58
|
hasProvider,
|
|
48
59
|
(uischema) => uischema?.options?.autocomplete === true,
|
|
49
60
|
),
|
|
@@ -65,7 +76,7 @@ const isArrayControl = (uischema: UISchemaElement, schema: unknown) => {
|
|
|
65
76
|
};
|
|
66
77
|
|
|
67
78
|
const providerMultiSelectTester = rankWith(
|
|
68
|
-
|
|
79
|
+
109, // Highest priority for array controls with providers
|
|
69
80
|
and(isArrayControl, hasProvider),
|
|
70
81
|
);
|
|
71
82
|
|
|
@@ -42,6 +42,7 @@ import type { ControlProps } from "@jsonforms/vue";
|
|
|
42
42
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
43
43
|
import { computed, getCurrentInstance } from "vue";
|
|
44
44
|
import { useDerive } from "../composables/useDerive";
|
|
45
|
+
import { useDeriveInitialValue } from "../composables/useDeriveInitialValue";
|
|
45
46
|
import { useProjection } from "../composables/useProjection";
|
|
46
47
|
import { useDirtyValidation } from "../composables/useDirtyValidation";
|
|
47
48
|
import InputNumber from "primevue/inputnumber";
|
|
@@ -70,6 +71,9 @@ const placeholder = computed<string | undefined>(
|
|
|
70
71
|
// Add derive functionality
|
|
71
72
|
useDerive({ control, handleChange, data: projectedData });
|
|
72
73
|
|
|
74
|
+
// Add deriveInitialValue — async API-based initial value seeding
|
|
75
|
+
useDeriveInitialValue({ control, handleChange });
|
|
76
|
+
|
|
73
77
|
// Currency and decimal configuration
|
|
74
78
|
const mode = computed(() => {
|
|
75
79
|
if (options.value.currency) return "currency";
|