@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
|
@@ -4,6 +4,8 @@ import _sfc_main$2 from "./JfNumber.vue.js";
|
|
|
4
4
|
import _sfc_main$3 from "./JfEnum.vue.js";
|
|
5
5
|
import _sfc_main$4 from "./JfEnumArray.vue.js";
|
|
6
6
|
import _sfc_main$5 from "./JfBoolean.vue.js";
|
|
7
|
+
import { getProjectedSchema } from "../../core/projection.js";
|
|
8
|
+
import { resolveScopeSchema } from "../../core/resolveScope.js";
|
|
7
9
|
const injectLayoutStyles = () => {
|
|
8
10
|
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
9
11
|
if (!document.getElementById("jsonforms-primevue-styles")) {
|
|
@@ -40,6 +42,7 @@ function registerPrimevueRenderers(jsonformsCore) {
|
|
|
40
42
|
isNumberControl,
|
|
41
43
|
isIntegerControl,
|
|
42
44
|
and,
|
|
45
|
+
or,
|
|
43
46
|
isControl,
|
|
44
47
|
schemaMatches,
|
|
45
48
|
isBooleanControl
|
|
@@ -61,27 +64,39 @@ function registerPrimevueRenderers(jsonformsCore) {
|
|
|
61
64
|
{}
|
|
62
65
|
);
|
|
63
66
|
};
|
|
67
|
+
const projectedSchemaMatches = (check) => (uischema, schema) => {
|
|
68
|
+
const ui = uischema;
|
|
69
|
+
const projection = ui?.options?.projection;
|
|
70
|
+
if (!projection || ui?.type !== "Control" || !ui?.scope) return false;
|
|
71
|
+
const propertySchema = resolveScopeSchema(ui.scope, schema);
|
|
72
|
+
if (!propertySchema) return false;
|
|
73
|
+
return check(getProjectedSchema(propertySchema, projection));
|
|
74
|
+
};
|
|
75
|
+
const isMultilineProjection = (uischema, schema) => {
|
|
76
|
+
const ui = uischema;
|
|
77
|
+
return ui?.options?.multi === true && projectedSchemaMatches((s) => s?.type === "string")(uischema, schema);
|
|
78
|
+
};
|
|
64
79
|
const renderers = [
|
|
65
80
|
// Multiline text has higher priority than regular text
|
|
66
|
-
{ tester: rankWith(PRIME + 4, isMultilineString), renderer: _sfc_main },
|
|
67
|
-
{ tester: rankWith(PRIME + 3, isStringControl), renderer: _sfc_main$1 },
|
|
81
|
+
{ tester: rankWith(PRIME + 4, or(isMultilineString, isMultilineProjection)), renderer: _sfc_main },
|
|
82
|
+
{ tester: rankWith(PRIME + 3, or(isStringControl, projectedSchemaMatches((s) => s?.type === "string"))), renderer: _sfc_main$1 },
|
|
68
83
|
{
|
|
69
|
-
tester: rankWith(PRIME + 6, isIntegerControl),
|
|
84
|
+
tester: rankWith(PRIME + 6, or(isIntegerControl, projectedSchemaMatches((s) => s?.type === "integer"))),
|
|
70
85
|
renderer: _sfc_main$2
|
|
71
86
|
},
|
|
72
87
|
{
|
|
73
|
-
tester: rankWith(PRIME + 4, isNumberControl),
|
|
88
|
+
tester: rankWith(PRIME + 4, or(isNumberControl, projectedSchemaMatches((s) => s?.type === "number"))),
|
|
74
89
|
renderer: _sfc_main$2
|
|
75
90
|
},
|
|
76
91
|
{
|
|
77
|
-
tester: rankWith(PRIME + 7, and(isControl, schemaMatches(isScalarEnum))),
|
|
92
|
+
tester: rankWith(PRIME + 7, or(and(isControl, schemaMatches(isScalarEnum)), and(isControl, projectedSchemaMatches(isScalarEnum)))),
|
|
78
93
|
renderer: _sfc_main$3
|
|
79
94
|
},
|
|
80
95
|
{
|
|
81
|
-
tester: rankWith(PRIME + 8, and(isControl, schemaMatches(isEnumArray))),
|
|
96
|
+
tester: rankWith(PRIME + 8, or(and(isControl, schemaMatches(isEnumArray)), and(isControl, projectedSchemaMatches(isEnumArray)))),
|
|
82
97
|
renderer: _sfc_main$4
|
|
83
98
|
},
|
|
84
|
-
{ tester: rankWith(PRIME + 3, isBooleanControl), renderer: _sfc_main$5 }
|
|
99
|
+
{ tester: rankWith(PRIME + 3, or(isBooleanControl, projectedSchemaMatches((s) => s?.type === "boolean"))), renderer: _sfc_main$5 }
|
|
85
100
|
];
|
|
86
101
|
primevueRenderers.splice(0, primevueRenderers.length, ...renderers);
|
|
87
102
|
renderersInitialized = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/vue/primevue/index.ts"],"sourcesContent":["import JfText from \"./JfText.vue\";\nimport JfTextArea from \"./JfTextArea.vue\";\nimport JfNumber from \"./JfNumber.vue\";\nimport JfEnum from \"./JfEnum.vue\";\nimport JfEnumArray from \"./JfEnumArray.vue\";\nimport JfBoolean from \"./JfBoolean.vue\";\n\n// Auto-inject layout styles\nconst injectLayoutStyles = () => {\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") {\n if (!document.getElementById(\"jsonforms-primevue-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"jsonforms-primevue-styles\";\n style.textContent = `\n/* JSONForms PrimeVue Provider Protocols Layout Styles */\n.vertical-layout {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 1rem;\n width: 100%;\n}\n\n.vertical-layout-item {\n width: 100%;\n}\n `;\n document.head.appendChild(style);\n }\n }\n};\n\n// Inject styles when this module is imported\ninjectLayoutStyles();\n\n// Track whether renderers have been registered to avoid duplicate registrations\nlet renderersInitialized = false;\n\n// Return empty renderers array initially\nexport const primevueRenderers: unknown[] = [];\n\n// Export a function that can be called by the consumer to register renderers\n// This should only be called once during application initialization\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function registerPrimevueRenderers(jsonformsCore: any): unknown[] {\n // Prevent duplicate registration which causes AJV schema recompilation\n if (renderersInitialized) {\n return primevueRenderers as unknown[];\n }\n\n const {\n rankWith,\n isStringControl,\n isNumberControl,\n isIntegerControl,\n and,\n isControl,\n schemaMatches,\n isBooleanControl,\n } = jsonformsCore;\n\n // Give PrimeVue renderers a high base rank; vanilla commonly uses small ranks (2–5)\n const PRIME = 100;\n\n // helpers for enum detection - using simple schema checks instead of JSONForms internal functions\n const isScalarEnum = (s?: object) => {\n const schema = s as {\n type?: string;\n enum?: unknown[];\n oneOf?: unknown[];\n };\n return (\n schema &&\n schema.type !== \"array\" &&\n (Array.isArray(schema.enum) || Array.isArray(schema.oneOf))\n );\n };\n\n const isEnumArray = (s?: object) => {\n const schema = s as {\n type?: string;\n items?: { enum?: unknown[]; oneOf?: unknown[] };\n };\n return (\n schema?.type === \"array\" &&\n (Array.isArray(schema.items?.enum) || Array.isArray(schema.items?.oneOf))\n );\n };\n\n // helper for multiline detection\n const isMultilineString = (uischema: unknown, schema: unknown) => {\n const controlUischema = uischema as { options?: { multi?: boolean } };\n return and(isStringControl, () => controlUischema?.options?.multi === true)(\n uischema as Parameters<typeof isStringControl>[0],\n schema as Parameters<typeof isStringControl>[1],\n {} as Parameters<typeof isStringControl>[2],\n );\n };\n\n const renderers = [\n // Multiline text has higher priority than regular text\n { tester: rankWith(PRIME + 4, isMultilineString), renderer: JfTextArea },\n { tester: rankWith(PRIME + 3, isStringControl), renderer: JfText },\n {\n tester: rankWith(PRIME + 6, isIntegerControl),\n renderer: JfNumber,\n },\n {\n tester: rankWith(PRIME + 4, isNumberControl),\n renderer: JfNumber,\n },\n {\n tester: rankWith(PRIME + 7, and(isControl, schemaMatches(isScalarEnum))),\n renderer: JfEnum,\n },\n {\n tester: rankWith(PRIME + 8, and(isControl, schemaMatches(isEnumArray))),\n renderer: JfEnumArray,\n },\n { tester: rankWith(PRIME + 3, isBooleanControl), renderer: JfBoolean },\n ];\n\n // Update the exported array\n primevueRenderers.splice(0, primevueRenderers.length, ...renderers);\n\n // Mark as initialized to prevent duplicate registrations\n renderersInitialized = true;\n\n return renderers;\n}\n\nexport { JfText, JfTextArea, JfNumber, JfEnum, JfEnumArray, JfBoolean };\n"],"names":["JfTextArea","JfText","JfNumber","JfEnum","JfEnumArray","JfBoolean"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/vue/primevue/index.ts"],"sourcesContent":["import JfText from \"./JfText.vue\";\nimport JfTextArea from \"./JfTextArea.vue\";\nimport JfNumber from \"./JfNumber.vue\";\nimport JfEnum from \"./JfEnum.vue\";\nimport JfEnumArray from \"./JfEnumArray.vue\";\nimport JfBoolean from \"./JfBoolean.vue\";\nimport { getProjectedSchema } from \"../../core/projection\";\nimport { resolveScopeSchema } from \"../../core/resolveScope\";\n\n// Auto-inject layout styles\nconst injectLayoutStyles = () => {\n if (typeof window !== \"undefined\" && typeof document !== \"undefined\") {\n if (!document.getElementById(\"jsonforms-primevue-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"jsonforms-primevue-styles\";\n style.textContent = `\n/* JSONForms PrimeVue Provider Protocols Layout Styles */\n.vertical-layout {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 1rem;\n width: 100%;\n}\n\n.vertical-layout-item {\n width: 100%;\n}\n `;\n document.head.appendChild(style);\n }\n }\n};\n\n// Inject styles when this module is imported\ninjectLayoutStyles();\n\n// Track whether renderers have been registered to avoid duplicate registrations\nlet renderersInitialized = false;\n\n// Return empty renderers array initially\nexport const primevueRenderers: unknown[] = [];\n\n// Export a function that can be called by the consumer to register renderers\n// This should only be called once during application initialization\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function registerPrimevueRenderers(jsonformsCore: any): unknown[] {\n // Prevent duplicate registration which causes AJV schema recompilation\n if (renderersInitialized) {\n return primevueRenderers as unknown[];\n }\n\n const {\n rankWith,\n isStringControl,\n isNumberControl,\n isIntegerControl,\n and,\n or,\n isControl,\n schemaMatches,\n isBooleanControl,\n } = jsonformsCore;\n\n // Give PrimeVue renderers a high base rank; vanilla commonly uses small ranks (2–5)\n const PRIME = 100;\n\n // helpers for enum detection - using simple schema checks instead of JSONForms internal functions\n const isScalarEnum = (s?: object) => {\n const schema = s as {\n type?: string;\n enum?: unknown[];\n oneOf?: unknown[];\n };\n return (\n schema &&\n schema.type !== \"array\" &&\n (Array.isArray(schema.enum) || Array.isArray(schema.oneOf))\n );\n };\n\n const isEnumArray = (s?: object) => {\n const schema = s as {\n type?: string;\n items?: { enum?: unknown[]; oneOf?: unknown[] };\n };\n return (\n schema?.type === \"array\" &&\n (Array.isArray(schema.items?.enum) || Array.isArray(schema.items?.oneOf))\n );\n };\n\n // helper for multiline detection\n const isMultilineString = (uischema: unknown, schema: unknown) => {\n const controlUischema = uischema as { options?: { multi?: boolean } };\n return and(isStringControl, () => controlUischema?.options?.multi === true)(\n uischema as Parameters<typeof isStringControl>[0],\n schema as Parameters<typeof isStringControl>[1],\n {} as Parameters<typeof isStringControl>[2],\n );\n };\n\n // Projection-aware schema check: when options.projection is set,\n // resolve the projected schema and test against it instead of the original\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const projectedSchemaMatches = (check: (schema: any) => boolean) =>\n (uischema: unknown, schema: unknown): boolean => {\n const ui = uischema as { type?: string; scope?: string; options?: { projection?: string } };\n const projection = ui?.options?.projection;\n if (!projection || ui?.type !== \"Control\" || !ui?.scope) return false;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const propertySchema = resolveScopeSchema(ui.scope, schema as Record<string, any>);\n if (!propertySchema) return false;\n\n return check(getProjectedSchema(propertySchema, projection));\n };\n\n const isMultilineProjection = (uischema: unknown, schema: unknown) => {\n const ui = uischema as { options?: { multi?: boolean } };\n return ui?.options?.multi === true &&\n projectedSchemaMatches((s) => s?.type === \"string\")(uischema, schema);\n };\n\n const renderers = [\n // Multiline text has higher priority than regular text\n { tester: rankWith(PRIME + 4, or(isMultilineString, isMultilineProjection)), renderer: JfTextArea },\n { tester: rankWith(PRIME + 3, or(isStringControl, projectedSchemaMatches((s) => s?.type === \"string\"))), renderer: JfText },\n {\n tester: rankWith(PRIME + 6, or(isIntegerControl, projectedSchemaMatches((s) => s?.type === \"integer\"))),\n renderer: JfNumber,\n },\n {\n tester: rankWith(PRIME + 4, or(isNumberControl, projectedSchemaMatches((s) => s?.type === \"number\"))),\n renderer: JfNumber,\n },\n {\n tester: rankWith(PRIME + 7, or(and(isControl, schemaMatches(isScalarEnum)), and(isControl, projectedSchemaMatches(isScalarEnum)))),\n renderer: JfEnum,\n },\n {\n tester: rankWith(PRIME + 8, or(and(isControl, schemaMatches(isEnumArray)), and(isControl, projectedSchemaMatches(isEnumArray)))),\n renderer: JfEnumArray,\n },\n { tester: rankWith(PRIME + 3, or(isBooleanControl, projectedSchemaMatches((s) => s?.type === \"boolean\"))), renderer: JfBoolean },\n ];\n\n // Update the exported array\n primevueRenderers.splice(0, primevueRenderers.length, ...renderers);\n\n // Mark as initialized to prevent duplicate registrations\n renderersInitialized = true;\n\n return renderers;\n}\n\nexport { JfText, JfTextArea, JfNumber, JfEnum, JfEnumArray, JfBoolean };\n"],"names":["JfTextArea","JfText","JfNumber","JfEnum","JfEnumArray","JfBoolean"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narrative.io/jsonforms-provider-protocols",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-beta.2",
|
|
4
4
|
"description": "Dynamic data provider capabilities for JSONForms with Vue 3 integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Narrative I/O",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"build:lib": "vite build",
|
|
59
59
|
"build:types": "vue-tsc -p tsconfig.build.json --declaration --emitDeclarationOnly",
|
|
60
60
|
"build:clean": "rm -rf dist",
|
|
61
|
+
"dev": "vite build --watch",
|
|
61
62
|
"prepare": "bun-git-hooks",
|
|
62
63
|
"prepublishOnly": "bun run build",
|
|
63
64
|
"test": "bun test",
|
|
@@ -65,13 +66,16 @@
|
|
|
65
66
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
|
66
67
|
"lint:check": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts",
|
|
67
68
|
"format": "prettier --write src/",
|
|
69
|
+
"check:built": "node scripts/ensureBuild.js",
|
|
70
|
+
"link:global": "bun run build && bun run check:built && bun link",
|
|
71
|
+
"pack:dist": "bun run build && bun run check:built && bun pm pack",
|
|
68
72
|
"demo": "cd demo && npm start",
|
|
69
73
|
"demo:install": "cd demo && npm install",
|
|
70
74
|
"demo:dev": "cd demo && npm run dev",
|
|
71
75
|
"demo:api": "cd demo && npm run api"
|
|
72
76
|
},
|
|
73
77
|
"devDependencies": {
|
|
74
|
-
"@commitlint/config-conventional": "^
|
|
78
|
+
"@commitlint/config-conventional": "^20.0.0",
|
|
75
79
|
"@eslint/js": "^9.34.0",
|
|
76
80
|
"@jsonforms/core": "^3.6.0",
|
|
77
81
|
"@jsonforms/vue": "^3.6.0",
|
|
@@ -81,7 +85,7 @@
|
|
|
81
85
|
"@vue/eslint-config-typescript": "^14.6.0",
|
|
82
86
|
"@vue/runtime-core": "^3.5.20",
|
|
83
87
|
"bun-git-hooks": "^0.2.19",
|
|
84
|
-
"commitlint": "^
|
|
88
|
+
"commitlint": "^20.0.0",
|
|
85
89
|
"eslint": "^9.34.0",
|
|
86
90
|
"eslint-config-prettier": "^10.1.8",
|
|
87
91
|
"eslint-plugin-vue": "^10.4.0",
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection utilities for navigating complex data structures
|
|
3
|
+
* through a dot-separated path where numeric segments are array indices.
|
|
4
|
+
*
|
|
5
|
+
* Examples:
|
|
6
|
+
* "0" → first element of an array
|
|
7
|
+
* "include" → the `include` property of an object
|
|
8
|
+
* "0.video_rate_usd" → nested property inside the first array element
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type ProjectionSegment = string | number;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse a projection path string into typed segments.
|
|
15
|
+
* Numeric strings become numbers (array indices), others stay as strings (object keys).
|
|
16
|
+
*/
|
|
17
|
+
export function parseProjectionPath(path: string): ProjectionSegment[] {
|
|
18
|
+
if (!path) return [];
|
|
19
|
+
return path.split(".").map((s) => {
|
|
20
|
+
const n = Number(s);
|
|
21
|
+
return Number.isInteger(n) && n >= 0 ? n : s;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Read a value from `data` by following the projection path.
|
|
27
|
+
* Returns `undefined` if any segment along the path is missing.
|
|
28
|
+
*/
|
|
29
|
+
export function getProjectedValue(
|
|
30
|
+
data: unknown,
|
|
31
|
+
path: string,
|
|
32
|
+
): unknown {
|
|
33
|
+
const segments = parseProjectionPath(path);
|
|
34
|
+
let current: unknown = data;
|
|
35
|
+
|
|
36
|
+
for (const seg of segments) {
|
|
37
|
+
if (current === null || current === undefined) return undefined;
|
|
38
|
+
|
|
39
|
+
if (typeof seg === "number") {
|
|
40
|
+
if (!Array.isArray(current)) return undefined;
|
|
41
|
+
current = current[seg];
|
|
42
|
+
} else {
|
|
43
|
+
if (typeof current !== "object") return undefined;
|
|
44
|
+
current = (current as Record<string, unknown>)[seg];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return current;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Immutably set a value at the projection path, preserving all sibling data.
|
|
53
|
+
* Constructs missing intermediate structures (arrays for numeric segments, objects for string segments).
|
|
54
|
+
*/
|
|
55
|
+
export function setProjectedValue(
|
|
56
|
+
data: unknown,
|
|
57
|
+
path: string,
|
|
58
|
+
value: unknown,
|
|
59
|
+
): unknown {
|
|
60
|
+
const segments = parseProjectionPath(path);
|
|
61
|
+
return setAtPath(data, segments, 0, value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function setAtPath(
|
|
65
|
+
current: unknown,
|
|
66
|
+
segments: ProjectionSegment[],
|
|
67
|
+
index: number,
|
|
68
|
+
value: unknown,
|
|
69
|
+
): unknown {
|
|
70
|
+
if (index === segments.length) {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const seg = segments[index]!;
|
|
75
|
+
|
|
76
|
+
if (typeof seg === "number") {
|
|
77
|
+
// Array index — ensure we have an array
|
|
78
|
+
const arr = Array.isArray(current) ? [...current] : [];
|
|
79
|
+
// Pad array if index is out of bounds
|
|
80
|
+
while (arr.length <= seg) {
|
|
81
|
+
arr.push(undefined);
|
|
82
|
+
}
|
|
83
|
+
arr[seg] = setAtPath(arr[seg], segments, index + 1, value);
|
|
84
|
+
return arr;
|
|
85
|
+
} else {
|
|
86
|
+
// Object key — ensure we have an object
|
|
87
|
+
const obj: Record<string, unknown> =
|
|
88
|
+
current !== null && current !== undefined && typeof current === "object" && !Array.isArray(current)
|
|
89
|
+
? { ...(current as Record<string, unknown>) }
|
|
90
|
+
: {};
|
|
91
|
+
obj[seg] = setAtPath(obj[seg], segments, index + 1, value);
|
|
92
|
+
return obj;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the schema at the projected path.
|
|
98
|
+
* Numeric segments traverse into `items` (array item schema).
|
|
99
|
+
* String segments traverse into `properties[segment]`.
|
|
100
|
+
*/
|
|
101
|
+
export function getProjectedSchema(
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
+
schema: Record<string, any>,
|
|
104
|
+
path: string,
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
106
|
+
): Record<string, any> {
|
|
107
|
+
const segments = parseProjectionPath(path);
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
109
|
+
let current: Record<string, any> = schema;
|
|
110
|
+
|
|
111
|
+
for (const seg of segments) {
|
|
112
|
+
if (!current) return {};
|
|
113
|
+
|
|
114
|
+
if (typeof seg === "number") {
|
|
115
|
+
// Array index → traverse into items schema
|
|
116
|
+
const items = current.items;
|
|
117
|
+
if (items && typeof items === "object") {
|
|
118
|
+
current = items as Record<string, unknown>;
|
|
119
|
+
} else {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
// Object key → traverse into properties[key]
|
|
124
|
+
const properties = current.properties as
|
|
125
|
+
| Record<string, Record<string, unknown>>
|
|
126
|
+
| undefined;
|
|
127
|
+
if (properties && properties[seg]) {
|
|
128
|
+
current = properties[seg];
|
|
129
|
+
} else {
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return current;
|
|
136
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a JSON Forms scope path to its schema within a root schema.
|
|
3
|
+
* Handles nested paths like "#/properties/parent/properties/child".
|
|
4
|
+
*
|
|
5
|
+
* Follows JSON Schema structure:
|
|
6
|
+
* - "properties" segments navigate into object `.properties`
|
|
7
|
+
* - "items" segments navigate into array `.items`
|
|
8
|
+
* - all other segments index directly into the current object
|
|
9
|
+
*/
|
|
10
|
+
export function resolveScopeSchema(
|
|
11
|
+
scope: string,
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
rootSchema: Record<string, any>,
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
): Record<string, any> | undefined {
|
|
16
|
+
if (!scope || !rootSchema) return undefined;
|
|
17
|
+
|
|
18
|
+
// Remove the leading "#/" and split into segments
|
|
19
|
+
const path = scope.replace(/^#\/?/, "");
|
|
20
|
+
if (!path) return rootSchema;
|
|
21
|
+
|
|
22
|
+
const segments = path.split("/");
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
let current: any = rootSchema;
|
|
25
|
+
|
|
26
|
+
for (const segment of segments) {
|
|
27
|
+
if (!current || typeof current !== "object") return undefined;
|
|
28
|
+
|
|
29
|
+
if (segment === "properties") {
|
|
30
|
+
current = current.properties;
|
|
31
|
+
} else if (segment === "items") {
|
|
32
|
+
current = current.items;
|
|
33
|
+
} else {
|
|
34
|
+
current = current[segment];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return current;
|
|
39
|
+
}
|
package/src/core/transforms.ts
CHANGED
|
@@ -14,30 +14,29 @@ export interface FlattenTransform extends Transform {
|
|
|
14
14
|
labelFormat?: string; // Optional format string like "{parent.name} → {name}"
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export type FilterOperator = "eq" | "neq" | "empty" | "notEmpty" | "gt" | "gte" | "lt" | "lte" | "contains";
|
|
18
|
+
|
|
19
|
+
export interface FilterCondition {
|
|
20
|
+
key: string;
|
|
21
|
+
operator?: FilterOperator; // Defaults to "eq" when values is provided, "exists" behavior when neither
|
|
22
|
+
values?: unknown[];
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
export interface FilterTransform extends Transform {
|
|
18
26
|
name: "filter";
|
|
19
|
-
key
|
|
20
|
-
values?: unknown[]; //
|
|
27
|
+
key?: string; // Legacy: single key to check
|
|
28
|
+
values?: unknown[]; // Legacy: single values array
|
|
29
|
+
conditions?: FilterCondition[]; // Multi-condition filter (AND logic)
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
export type TransformStep = FlattenTransform | FilterTransform;
|
|
24
33
|
|
|
25
34
|
export type TransformPipeline = TransformStep[];
|
|
26
35
|
|
|
27
|
-
/**
|
|
28
|
-
* Registry of transform functions
|
|
29
|
-
*/
|
|
30
36
|
type TransformFunction = (items: unknown[], config: Transform) => unknown[];
|
|
31
37
|
|
|
32
38
|
const transformRegistry: Record<string, TransformFunction> = {};
|
|
33
39
|
|
|
34
|
-
/**
|
|
35
|
-
* Register a transform function
|
|
36
|
-
*/
|
|
37
|
-
export function registerTransform(name: string, fn: TransformFunction): void {
|
|
38
|
-
transformRegistry[name] = fn;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
40
|
/**
|
|
42
41
|
* Apply a pipeline of transforms to data
|
|
43
42
|
*/
|
|
@@ -124,29 +123,95 @@ function flattenTransform(items: unknown[], config: Transform): unknown[] {
|
|
|
124
123
|
return flattened;
|
|
125
124
|
}
|
|
126
125
|
|
|
126
|
+
function isEmpty(value: unknown): boolean {
|
|
127
|
+
if (value === null || value === undefined) return true;
|
|
128
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
129
|
+
if (typeof value === "string") return value.length === 0;
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function evaluateCondition(
|
|
134
|
+
itemObj: Record<string, unknown>,
|
|
135
|
+
condition: FilterCondition,
|
|
136
|
+
): boolean {
|
|
137
|
+
const value = itemObj[condition.key];
|
|
138
|
+
const operator = condition.operator ?? (condition.values ? "eq" : "eq");
|
|
139
|
+
|
|
140
|
+
switch (operator) {
|
|
141
|
+
case "eq":
|
|
142
|
+
if (!condition.values || condition.values.length === 0) {
|
|
143
|
+
return condition.key in itemObj;
|
|
144
|
+
}
|
|
145
|
+
return condition.values.includes(value);
|
|
146
|
+
case "neq":
|
|
147
|
+
if (!condition.values || condition.values.length === 0) {
|
|
148
|
+
return !(condition.key in itemObj);
|
|
149
|
+
}
|
|
150
|
+
return !condition.values.includes(value);
|
|
151
|
+
case "empty":
|
|
152
|
+
return isEmpty(value);
|
|
153
|
+
case "notEmpty":
|
|
154
|
+
return !isEmpty(value);
|
|
155
|
+
case "gt":
|
|
156
|
+
return typeof value === "number" && condition.values !== undefined && value > (condition.values[0] as number);
|
|
157
|
+
case "gte":
|
|
158
|
+
return typeof value === "number" && condition.values !== undefined && value >= (condition.values[0] as number);
|
|
159
|
+
case "lt":
|
|
160
|
+
return typeof value === "number" && condition.values !== undefined && value < (condition.values[0] as number);
|
|
161
|
+
case "lte":
|
|
162
|
+
return typeof value === "number" && condition.values !== undefined && value <= (condition.values[0] as number);
|
|
163
|
+
case "contains":
|
|
164
|
+
if (typeof value === "string" && condition.values) {
|
|
165
|
+
return condition.values.some((v) => value.includes(String(v)));
|
|
166
|
+
}
|
|
167
|
+
if (Array.isArray(value) && condition.values) {
|
|
168
|
+
return condition.values.some((v) => value.includes(v));
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
default:
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
127
176
|
/**
|
|
128
|
-
* Filter transform - filters items based on
|
|
177
|
+
* Filter transform - filters items based on conditions
|
|
178
|
+
*
|
|
179
|
+
* Supports legacy single key/values syntax and new multi-condition syntax.
|
|
180
|
+
* When using conditions, all conditions must match (AND logic).
|
|
181
|
+
*
|
|
182
|
+
* Operators:
|
|
183
|
+
* eq - item[key] matches one of values (default)
|
|
184
|
+
* neq - item[key] does NOT match any of values
|
|
185
|
+
* empty - item[key] is null, undefined, empty array, or empty string
|
|
186
|
+
* notEmpty - inverse of empty
|
|
187
|
+
* gt - item[key] > values[0]
|
|
188
|
+
* gte - item[key] >= values[0]
|
|
189
|
+
* lt - item[key] < values[0]
|
|
190
|
+
* lte - item[key] <= values[0]
|
|
191
|
+
* contains - string includes substring, or array includes value
|
|
129
192
|
*/
|
|
130
193
|
function filterTransform(items: unknown[], config: Transform): unknown[] {
|
|
131
194
|
const filterConfig = config as FilterTransform;
|
|
132
|
-
|
|
195
|
+
|
|
196
|
+
// Build conditions array from either new or legacy syntax
|
|
197
|
+
let conditions: FilterCondition[];
|
|
198
|
+
|
|
199
|
+
if (filterConfig.conditions) {
|
|
200
|
+
conditions = filterConfig.conditions;
|
|
201
|
+
} else if (filterConfig.key) {
|
|
202
|
+
// Legacy single key/values syntax
|
|
203
|
+
conditions = [{ key: filterConfig.key, values: filterConfig.values }];
|
|
204
|
+
} else {
|
|
205
|
+
return items;
|
|
206
|
+
}
|
|
133
207
|
|
|
134
208
|
return items.filter((item) => {
|
|
135
209
|
if (typeof item !== "object" || item === null) return false;
|
|
136
|
-
|
|
137
210
|
const itemObj = item as Record<string, unknown>;
|
|
138
|
-
|
|
139
|
-
// If no values array provided, just check if the key exists
|
|
140
|
-
if (!values || values.length === 0) {
|
|
141
|
-
return key in itemObj;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// If values array provided, check if item[key] matches any of the values
|
|
145
|
-
const itemValue = itemObj[key];
|
|
146
|
-
return values.includes(itemValue);
|
|
211
|
+
return conditions.every((condition) => evaluateCondition(itemObj, condition));
|
|
147
212
|
});
|
|
148
213
|
}
|
|
149
214
|
|
|
150
215
|
// Register built-in transforms
|
|
151
|
-
|
|
152
|
-
|
|
216
|
+
transformRegistry["flatten"] = flattenTransform;
|
|
217
|
+
transformRegistry["filter"] = filterTransform;
|
package/src/core/types.ts
CHANGED
|
@@ -44,3 +44,11 @@ export interface AuthConfig {
|
|
|
44
44
|
token?: string | (() => string); // Generic token auth
|
|
45
45
|
[key: string]: unknown; // Custom auth fields
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
export interface ConnectorDataLayer {
|
|
49
|
+
dataset_name?: string;
|
|
50
|
+
dataset_description?: string;
|
|
51
|
+
dataset_id?: number;
|
|
52
|
+
profile_id?: string;
|
|
53
|
+
profile_name?: string;
|
|
54
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,11 +3,15 @@ import { cache as globalCache } from "./core/cache";
|
|
|
3
3
|
import { registry as globalRegistry } from "./core/registry";
|
|
4
4
|
import type { Protocol, AuthConfig } from "./core/types";
|
|
5
5
|
|
|
6
|
+
console.log("[jsonforms-provider-protocols] loaded v2.10.0 (symlink)");
|
|
7
|
+
|
|
6
8
|
export { cache } from "./core/cache";
|
|
7
9
|
export * from "./core/jsonpath";
|
|
8
10
|
export { registry } from "./core/registry";
|
|
9
11
|
export * from "./core/templating";
|
|
10
12
|
export * from "./core/transforms";
|
|
13
|
+
export * from "./core/projection";
|
|
14
|
+
export * from "./core/resolveScope";
|
|
11
15
|
// Core exports
|
|
12
16
|
export * from "./core/types";
|
|
13
17
|
|
|
@@ -22,6 +26,8 @@ export {
|
|
|
22
26
|
ProviderSelect,
|
|
23
27
|
ProviderMultiSelect,
|
|
24
28
|
useProvider,
|
|
29
|
+
createDataLayer,
|
|
30
|
+
useDataLayer,
|
|
25
31
|
JfText,
|
|
26
32
|
JfTextArea,
|
|
27
33
|
JfNumber,
|
|
@@ -29,6 +35,7 @@ export {
|
|
|
29
35
|
JfEnumArray,
|
|
30
36
|
JfBoolean,
|
|
31
37
|
} from "./vue";
|
|
38
|
+
export type { DataLayer } from "./vue";
|
|
32
39
|
|
|
33
40
|
export interface ProviderConfig {
|
|
34
41
|
protocols?: Protocol[];
|
|
@@ -3,6 +3,7 @@ import type { ControlElement, JsonSchema } from "@jsonforms/core";
|
|
|
3
3
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
4
4
|
import { computed, ref, watch } from "vue";
|
|
5
5
|
import { useProvider } from "../composables/useProvider";
|
|
6
|
+
import { useProjection } from "../composables/useProjection";
|
|
6
7
|
import AutoComplete from "primevue/autocomplete";
|
|
7
8
|
|
|
8
9
|
const props = defineProps<{
|
|
@@ -10,7 +11,8 @@ const props = defineProps<{
|
|
|
10
11
|
schema: JsonSchema;
|
|
11
12
|
path: string;
|
|
12
13
|
}>();
|
|
13
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
14
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
15
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
14
16
|
|
|
15
17
|
const binding = computed(() => {
|
|
16
18
|
const provider = control.value.uischema?.options?.provider;
|
|
@@ -33,7 +35,7 @@ watch(query, () => {
|
|
|
33
35
|
});
|
|
34
36
|
|
|
35
37
|
const value = computed({
|
|
36
|
-
get: () =>
|
|
38
|
+
get: () => projectedData.value,
|
|
37
39
|
set: (v) => handleChange(control.value.path, v),
|
|
38
40
|
});
|
|
39
41
|
|
|
@@ -3,6 +3,7 @@ import type { ControlElement, JsonSchema } from "@jsonforms/core";
|
|
|
3
3
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
4
4
|
import { computed, inject, watch } from "vue";
|
|
5
5
|
import { useProvider } from "../composables/useProvider";
|
|
6
|
+
import { useProjection } from "../composables/useProjection";
|
|
6
7
|
import { shouldAutoSelectMulti } from "../utils/autoSelect";
|
|
7
8
|
import MultiSelect from "primevue/multiselect";
|
|
8
9
|
|
|
@@ -11,7 +12,8 @@ const props = defineProps<{
|
|
|
11
12
|
schema: JsonSchema;
|
|
12
13
|
path: string;
|
|
13
14
|
}>();
|
|
14
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
15
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
16
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
15
17
|
|
|
16
18
|
const binding = computed(() => {
|
|
17
19
|
const provider = control.value.uischema?.options?.provider;
|
|
@@ -53,7 +55,7 @@ watch(
|
|
|
53
55
|
control.value.uischema?.options?.autoSelectSingle === true,
|
|
54
56
|
isLoading,
|
|
55
57
|
items: newItems,
|
|
56
|
-
currentValue: Array.isArray(
|
|
58
|
+
currentValue: Array.isArray(projectedData.value) ? projectedData.value : [],
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
if (valueToSelect !== null) {
|
|
@@ -74,13 +76,13 @@ const sameSet = (a: unknown[], b: unknown[]) => {
|
|
|
74
76
|
// v-model with guard to avoid recursive updates
|
|
75
77
|
const value = computed({
|
|
76
78
|
get() {
|
|
77
|
-
const curr = Array.isArray(
|
|
79
|
+
const curr = Array.isArray(projectedData.value) ? projectedData.value : [];
|
|
78
80
|
// return a fresh copy so MultiSelect can't mutate JSONForms' array in place
|
|
79
81
|
return [...curr];
|
|
80
82
|
},
|
|
81
83
|
set(val) {
|
|
82
84
|
const next = Array.isArray(val) ? [...val] : [];
|
|
83
|
-
const curr = Array.isArray(
|
|
85
|
+
const curr = Array.isArray(projectedData.value) ? projectedData.value : [];
|
|
84
86
|
if (!sameSet(curr, next)) handleChange(control.value.path, next);
|
|
85
87
|
},
|
|
86
88
|
});
|
|
@@ -3,6 +3,7 @@ import type { ControlElement, JsonSchema } from "@jsonforms/core";
|
|
|
3
3
|
import { useJsonFormsControl } from "@jsonforms/vue";
|
|
4
4
|
import { computed, inject, watch } from "vue";
|
|
5
5
|
import { useProvider } from "../composables/useProvider";
|
|
6
|
+
import { useProjection } from "../composables/useProjection";
|
|
6
7
|
import { shouldAutoSelect } from "../utils/autoSelect";
|
|
7
8
|
import Dropdown from "primevue/dropdown";
|
|
8
9
|
|
|
@@ -11,7 +12,8 @@ const props = defineProps<{
|
|
|
11
12
|
schema: JsonSchema;
|
|
12
13
|
path: string;
|
|
13
14
|
}>();
|
|
14
|
-
const { control, handleChange } = useJsonFormsControl(props);
|
|
15
|
+
const { control, handleChange: rawHandleChange } = useJsonFormsControl(props);
|
|
16
|
+
const { projectedData, handleProjectedChange: handleChange } = useProjection(control, rawHandleChange);
|
|
15
17
|
|
|
16
18
|
const binding = computed(() => {
|
|
17
19
|
const provider = control.value.uischema?.options?.provider;
|
|
@@ -53,7 +55,7 @@ watch(
|
|
|
53
55
|
control.value.uischema?.options?.autoSelectSingle !== false,
|
|
54
56
|
isLoading,
|
|
55
57
|
items: newItems,
|
|
56
|
-
currentValue:
|
|
58
|
+
currentValue: projectedData.value,
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
if (valueToSelect !== null) {
|
|
@@ -64,7 +66,7 @@ watch(
|
|
|
64
66
|
);
|
|
65
67
|
|
|
66
68
|
const value = computed({
|
|
67
|
-
get: () =>
|
|
69
|
+
get: () => projectedData.value,
|
|
68
70
|
set: (v) => handleChange(control.value.path, v),
|
|
69
71
|
});
|
|
70
72
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ref,
|
|
3
|
+
provide,
|
|
4
|
+
inject,
|
|
5
|
+
readonly,
|
|
6
|
+
type DeepReadonly,
|
|
7
|
+
type Ref,
|
|
8
|
+
} from "vue";
|
|
9
|
+
import type { ConnectorDataLayer } from "../../core/types";
|
|
10
|
+
|
|
11
|
+
const DATA_LAYER_KEY = Symbol("dataLayer");
|
|
12
|
+
|
|
13
|
+
export interface DataLayer {
|
|
14
|
+
push(data: Partial<ConnectorDataLayer>): void;
|
|
15
|
+
state: DeepReadonly<Ref<ConnectorDataLayer>>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createDataLayer(): DataLayer {
|
|
19
|
+
const state = ref<ConnectorDataLayer>({});
|
|
20
|
+
|
|
21
|
+
const dataLayer: DataLayer = {
|
|
22
|
+
push(data: Partial<ConnectorDataLayer>) {
|
|
23
|
+
state.value = { ...state.value, ...data };
|
|
24
|
+
},
|
|
25
|
+
state: readonly(state) as DeepReadonly<Ref<ConnectorDataLayer>>,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
provide(DATA_LAYER_KEY, dataLayer);
|
|
29
|
+
|
|
30
|
+
return dataLayer;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function useDataLayer(): DeepReadonly<Ref<ConnectorDataLayer>> {
|
|
34
|
+
const dataLayer = inject<DataLayer | null>(DATA_LAYER_KEY, null);
|
|
35
|
+
if (dataLayer) {
|
|
36
|
+
return dataLayer.state;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// No dataLayer provided — return empty reactive ref
|
|
40
|
+
return readonly(ref<ConnectorDataLayer>({})) as DeepReadonly<
|
|
41
|
+
Ref<ConnectorDataLayer>
|
|
42
|
+
>;
|
|
43
|
+
}
|