@koumoul/vjsf 3.0.0-beta.27 → 3.0.0-beta.28
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koumoul/vjsf",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.28",
|
|
4
4
|
"description": "Generate forms for the vuetify UI library (vuejs) based on annotated JSON schemas.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "vitest",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"vuetify": "^3.6.8"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@json-layout/core": "0.
|
|
79
|
+
"@json-layout/core": "0.24.0",
|
|
80
80
|
"@vueuse/core": "^10.5.0",
|
|
81
81
|
"debug": "^4.3.4",
|
|
82
82
|
"ejs": "^3.1.9"
|
|
@@ -12,9 +12,12 @@ export const defaultOptions = {
|
|
|
12
12
|
* @param {number} width
|
|
13
13
|
* @param {import("vue").Slots} slots
|
|
14
14
|
* @param {Record<string, import('vue').Component>} defaultNodeComponents
|
|
15
|
+
* @param {(data: any) => void} onData
|
|
16
|
+
* @param {(statefulLayout: import('@json-layout/core').StatefulLayout) => void} onUpdate
|
|
17
|
+
* @param {(key: string) => void} onAutofocus
|
|
15
18
|
* @returns
|
|
16
19
|
*/
|
|
17
|
-
export const getFullOptions = (options, form, width, slots, defaultNodeComponents) => {
|
|
20
|
+
export const getFullOptions = (options, form, width, slots, defaultNodeComponents, onData, onUpdate, onAutofocus) => {
|
|
18
21
|
const components = { ...options?.components }
|
|
19
22
|
const nodeComponents = { ...defaultNodeComponents, ...options?.nodeComponents }
|
|
20
23
|
if (options?.plugins) {
|
|
@@ -28,6 +31,9 @@ export const getFullOptions = (options, form, width, slots, defaultNodeComponent
|
|
|
28
31
|
...defaultOptions,
|
|
29
32
|
readOnly: !!(form && (form.isDisabled.value || form.isReadonly.value)),
|
|
30
33
|
...options,
|
|
34
|
+
onData,
|
|
35
|
+
onUpdate,
|
|
36
|
+
onAutofocus,
|
|
31
37
|
context: options?.context ? JSON.parse(JSON.stringify(options.context)) : {},
|
|
32
38
|
width: Math.round(width ?? 0),
|
|
33
39
|
vjsfSlots: { ...slots },
|
|
@@ -3,6 +3,9 @@ import { inject, toRaw, shallowRef, computed, ref, watch, useSlots } from 'vue'
|
|
|
3
3
|
import { useElementSize } from '@vueuse/core'
|
|
4
4
|
import { getFullOptions } from '../components/options.js'
|
|
5
5
|
import { setAutoFreeze } from 'immer'
|
|
6
|
+
import Debug from 'debug'
|
|
7
|
+
|
|
8
|
+
const debug = Debug('vjsf:use-vjsf')
|
|
6
9
|
|
|
7
10
|
// immer freezing is disabled because it is not compatible with Vue 3 reactivity
|
|
8
11
|
setAutoFreeze(false)
|
|
@@ -54,60 +57,65 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
|
|
|
54
57
|
|
|
55
58
|
const slots = useSlots()
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const compiledLayout = computed(() => {
|
|
69
|
-
if (precompiledLayout?.value) return precompiledLayout?.value
|
|
70
|
-
if (!compile) throw new Error('compile function is not available')
|
|
71
|
-
const compiledLayout = compile(schema.value, compileOptions.value)
|
|
72
|
-
return compiledLayout
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
const onStatefulLayoutUpdate = () => {
|
|
76
|
-
if (!statefulLayout.value) return
|
|
77
|
-
stateTree.value = statefulLayout.value.stateTree
|
|
78
|
-
emit('update:state', statefulLayout.value)
|
|
60
|
+
/* Callbacks from json layout stateful layout */
|
|
61
|
+
/**
|
|
62
|
+
* @param {import('../types.js').VjsfStatefulLayout} statefulLayout
|
|
63
|
+
*/
|
|
64
|
+
const onStatefulLayoutUpdate = (statefulLayout) => {
|
|
65
|
+
debug('onStatefulLayoutUpdate', statefulLayout)
|
|
66
|
+
if (!statefulLayout) return
|
|
67
|
+
stateTree.value = statefulLayout.stateTree
|
|
68
|
+
debug(' -> emit update:state')
|
|
69
|
+
emit('update:state', statefulLayout)
|
|
79
70
|
if (form) {
|
|
80
71
|
// cf https://vuetifyjs.com/en/components/forms/#validation-state
|
|
81
|
-
if (statefulLayout.
|
|
82
|
-
else if (statefulLayout.
|
|
72
|
+
if (statefulLayout.valid) form.update('vjsf', true, [])
|
|
73
|
+
else if (statefulLayout.hasHiddenError) form.update('vjsf', null, [])
|
|
83
74
|
else form.update('vjsf', false, [])
|
|
84
75
|
}
|
|
85
76
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
/**
|
|
78
|
+
* @param {any} data
|
|
79
|
+
*/
|
|
80
|
+
const onDataUpdate = (data) => {
|
|
81
|
+
debug('onDataUpdate', data)
|
|
82
|
+
debug(' -> emit update:modelValue')
|
|
83
|
+
emit('update:modelValue', data)
|
|
91
84
|
}
|
|
92
|
-
|
|
93
85
|
const onAutofocus = () => {
|
|
94
86
|
if (!el.value) return
|
|
95
87
|
// @ts-ignore
|
|
96
88
|
const autofocusNodeElement = el.value.querySelector('.vjsf-input--autofocus')
|
|
89
|
+
debug('onAutofocus', autofocusNodeElement)
|
|
97
90
|
if (autofocusNodeElement) {
|
|
98
91
|
const autofocusInputElement = autofocusNodeElement.querySelector('input') ?? autofocusNodeElement.querySelector('textarea:not([style*="display: none"]')
|
|
99
92
|
if (autofocusInputElement) autofocusInputElement.focus()
|
|
100
93
|
}
|
|
101
94
|
}
|
|
102
95
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
const fullOptions = computed(() => getFullOptions(options.value, form, width.value, slots, { ...nodeComponents }, onDataUpdate, onStatefulLayoutUpdate, onAutofocus))
|
|
97
|
+
|
|
98
|
+
// do not use a simple computed here as we want to prevent recompiling the layout when the options are the same
|
|
99
|
+
/** @type {import('vue').Ref<import('@json-layout/core').PartialCompileOptions>} */
|
|
100
|
+
const compileOptions = ref({})
|
|
101
|
+
watch(fullOptions, (newOptions) => {
|
|
102
|
+
if (precompiledLayout?.value) return
|
|
103
|
+
const newCompileOptions = produceCompileOptions(compileOptions.value, newOptions)
|
|
104
|
+
if (newCompileOptions !== compileOptions.value) {
|
|
105
|
+
debug('new compileOptions', newCompileOptions)
|
|
106
|
+
compileOptions.value = newCompileOptions
|
|
109
107
|
}
|
|
108
|
+
}, { immediate: true })
|
|
110
109
|
|
|
110
|
+
const compiledLayout = computed(() => {
|
|
111
|
+
if (precompiledLayout?.value) return precompiledLayout?.value
|
|
112
|
+
if (!compile) throw new Error('compile function is not available')
|
|
113
|
+
const compiledLayout = compile(schema.value, compileOptions.value)
|
|
114
|
+
return compiledLayout
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const initStatefulLayout = () => {
|
|
118
|
+
if (!width.value) return
|
|
111
119
|
// @ts-ignore
|
|
112
120
|
statefulLayout.value = /** @type {import('../types.js').VjsfStatefulLayout} */(new StatefulLayout(
|
|
113
121
|
toRaw(compiledLayout.value),
|
|
@@ -115,29 +123,34 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
|
|
|
115
123
|
toRaw(fullOptions.value),
|
|
116
124
|
toRaw(modelValue.value)
|
|
117
125
|
))
|
|
118
|
-
onStatefulLayoutUpdate()
|
|
119
|
-
onDataUpdate()
|
|
120
|
-
statefulLayout.value.events.on('update', onStatefulLayoutUpdate)
|
|
121
|
-
statefulLayout.value.events.on('data', onDataUpdate)
|
|
122
|
-
statefulLayout.value.events.on('autofocus', onAutofocus)
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
// case where options are updated from outside
|
|
126
129
|
watch(fullOptions, (newOptions) => {
|
|
130
|
+
debug('watch fullOptions', fullOptions)
|
|
127
131
|
if (statefulLayout.value) {
|
|
132
|
+
debug(' -> update statefulLayout options')
|
|
128
133
|
statefulLayout.value.options = newOptions
|
|
129
134
|
} else {
|
|
135
|
+
debug(' -> init statefulLayout')
|
|
130
136
|
initStatefulLayout()
|
|
131
137
|
}
|
|
132
138
|
})
|
|
133
139
|
|
|
134
140
|
// case where data is updated from outside
|
|
135
141
|
watch(modelValue, (newData) => {
|
|
136
|
-
|
|
142
|
+
const rawData = toRaw(newData)
|
|
143
|
+
if (statefulLayout.value && statefulLayout.value.data !== rawData) {
|
|
144
|
+
debug('modelValue changed from outside', rawData)
|
|
145
|
+
debug(' -> update statefulLayout data')
|
|
146
|
+
statefulLayout.value.data = toRaw(rawData)
|
|
147
|
+
}
|
|
137
148
|
})
|
|
138
149
|
|
|
139
150
|
// case where schema or compile options are updated from outside
|
|
140
151
|
watch(compiledLayout, (newCompiledLayout) => {
|
|
152
|
+
debug('watch compiledLayout', newCompiledLayout)
|
|
153
|
+
debug(' -> init statefulLayout')
|
|
141
154
|
initStatefulLayout()
|
|
142
155
|
})
|
|
143
156
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** @type {import("../types.js").PartialVjsfOptions} */
|
|
2
2
|
export const defaultOptions: import("../types.js").PartialVjsfOptions;
|
|
3
|
-
export function getFullOptions(options: Partial<import("../types.js").VjsfOptions> | null, form: any, width: number, slots: import("vue").Slots, defaultNodeComponents: Record<string, import('vue').Component
|
|
3
|
+
export function getFullOptions(options: Partial<import("../types.js").VjsfOptions> | null, form: any, width: number, slots: import("vue").Slots, defaultNodeComponents: Record<string, import('vue').Component>, onData: (data: any) => void, onUpdate: (statefulLayout: import('@json-layout/core').StatefulLayout) => void, onAutofocus: (key: string) => void): import("../types.js").VjsfOptions;
|
|
4
4
|
//# sourceMappingURL=options.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/components/options.js"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,6BADW,OAAO,aAAa,EAAE,kBAAkB,CAKlD;
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/components/options.js"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,6BADW,OAAO,aAAa,EAAE,kBAAkB,CAKlD;AAcM,wCAVI,QAAQ,OAAO,aAAa,EAAE,WAAW,CAAC,GAAG,IAAI,QACjD,GAAG,SACH,MAAM,SACN,OAAO,KAAK,EAAE,KAAK,yBACnB,OAAO,MAAM,EAAE,OAAO,KAAK,EAAE,SAAS,CAAC,iBAChC,GAAG,KAAK,IAAI,6BACF,OAAO,mBAAmB,EAAE,cAAc,KAAK,IAAI,qBAC9D,MAAM,KAAK,IAAI,qCA2B/B"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
declare const _default: import("vue").DefineComponent<{}, {
|
|
2
|
-
$emit: ((event: "update:
|
|
2
|
+
$emit: ((event: "update:state", state: import("../types.js").VjsfStatefulLayout) => void) & ((event: "update:modelValue", data: any) => void);
|
|
3
|
+
options: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null;
|
|
3
4
|
schema: Record<string, any>;
|
|
4
5
|
modelValue: any;
|
|
5
|
-
options: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null;
|
|
6
6
|
precompiledLayout: import("../../../node_modules/@json-layout/core/types/compile/types.js").CompiledLayout;
|
|
7
7
|
$props: {
|
|
8
|
+
readonly options?: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null | undefined;
|
|
8
9
|
readonly schema?: Record<string, any> | undefined;
|
|
9
10
|
readonly modelValue?: any;
|
|
10
|
-
readonly options?: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null | undefined;
|
|
11
11
|
readonly precompiledLayout?: import("../../../node_modules/@json-layout/core/types/compile/types.js").CompiledLayout | undefined;
|
|
12
12
|
};
|
|
13
13
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-vjsf.d.ts","sourceRoot":"","sources":["../../src/composables/use-vjsf.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-vjsf.d.ts","sourceRoot":"","sources":["../../src/composables/use-vjsf.js"],"names":[],"mappings":"AAYA;IACE;;MAEE;gCADO,GAAG;IAGZ;;MAEE;4BADO,OAAO,aAAa,EAAE,kBAAkB;EAGlD;AAWM,gCARI,OAAO,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,cACzB,OAAO,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,WACtB,OAAO,KAAK,EAAE,GAAG,CAAC,OAAO,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC,kBAClE,OAAO,MAAM,EAAE,OAAO,KAAK,EAAE,SAAS,CAAC,QACvC,GAAG;;;;EAiIb"}
|