@koumoul/vjsf 3.0.0-beta.26 → 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,10 +12,13 @@ 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) => {
|
|
18
|
-
const components = options?.components
|
|
20
|
+
export const getFullOptions = (options, form, width, slots, defaultNodeComponents, onData, onUpdate, onAutofocus) => {
|
|
21
|
+
const components = { ...options?.components }
|
|
19
22
|
const nodeComponents = { ...defaultNodeComponents, ...options?.nodeComponents }
|
|
20
23
|
if (options?.plugins) {
|
|
21
24
|
for (const plugin of 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,7 +57,43 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
|
|
|
54
57
|
|
|
55
58
|
const slots = useSlots()
|
|
56
59
|
|
|
57
|
-
|
|
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)
|
|
70
|
+
if (form) {
|
|
71
|
+
// cf https://vuetifyjs.com/en/components/forms/#validation-state
|
|
72
|
+
if (statefulLayout.valid) form.update('vjsf', true, [])
|
|
73
|
+
else if (statefulLayout.hasHiddenError) form.update('vjsf', null, [])
|
|
74
|
+
else form.update('vjsf', false, [])
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* @param {any} data
|
|
79
|
+
*/
|
|
80
|
+
const onDataUpdate = (data) => {
|
|
81
|
+
debug('onDataUpdate', data)
|
|
82
|
+
debug(' -> emit update:modelValue')
|
|
83
|
+
emit('update:modelValue', data)
|
|
84
|
+
}
|
|
85
|
+
const onAutofocus = () => {
|
|
86
|
+
if (!el.value) return
|
|
87
|
+
// @ts-ignore
|
|
88
|
+
const autofocusNodeElement = el.value.querySelector('.vjsf-input--autofocus')
|
|
89
|
+
debug('onAutofocus', autofocusNodeElement)
|
|
90
|
+
if (autofocusNodeElement) {
|
|
91
|
+
const autofocusInputElement = autofocusNodeElement.querySelector('input') ?? autofocusNodeElement.querySelector('textarea:not([style*="display: none"]')
|
|
92
|
+
if (autofocusInputElement) autofocusInputElement.focus()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const fullOptions = computed(() => getFullOptions(options.value, form, width.value, slots, { ...nodeComponents }, onDataUpdate, onStatefulLayoutUpdate, onAutofocus))
|
|
58
97
|
|
|
59
98
|
// do not use a simple computed here as we want to prevent recompiling the layout when the options are the same
|
|
60
99
|
/** @type {import('vue').Ref<import('@json-layout/core').PartialCompileOptions>} */
|
|
@@ -62,7 +101,10 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
|
|
|
62
101
|
watch(fullOptions, (newOptions) => {
|
|
63
102
|
if (precompiledLayout?.value) return
|
|
64
103
|
const newCompileOptions = produceCompileOptions(compileOptions.value, newOptions)
|
|
65
|
-
if (newCompileOptions !== compileOptions.value)
|
|
104
|
+
if (newCompileOptions !== compileOptions.value) {
|
|
105
|
+
debug('new compileOptions', newCompileOptions)
|
|
106
|
+
compileOptions.value = newCompileOptions
|
|
107
|
+
}
|
|
66
108
|
}, { immediate: true })
|
|
67
109
|
|
|
68
110
|
const compiledLayout = computed(() => {
|
|
@@ -72,71 +114,43 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
|
|
|
72
114
|
return compiledLayout
|
|
73
115
|
})
|
|
74
116
|
|
|
75
|
-
const onStatefulLayoutUpdate = () => {
|
|
76
|
-
if (!statefulLayout.value) return
|
|
77
|
-
stateTree.value = statefulLayout.value.stateTree
|
|
78
|
-
emit('update:state', statefulLayout.value)
|
|
79
|
-
if (form) {
|
|
80
|
-
// cf https://vuetifyjs.com/en/components/forms/#validation-state
|
|
81
|
-
if (statefulLayout.value.valid) form.update('vjsf', true, [])
|
|
82
|
-
else if (statefulLayout.value.hasHiddenError) form.update('vjsf', null, [])
|
|
83
|
-
else form.update('vjsf', false, [])
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const onDataUpdate = () => {
|
|
88
|
-
if (statefulLayout.value && modelValue !== statefulLayout.value.data) {
|
|
89
|
-
emit('update:modelValue', statefulLayout.value.data)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
117
|
const initStatefulLayout = () => {
|
|
94
118
|
if (!width.value) return
|
|
95
|
-
|
|
96
119
|
// @ts-ignore
|
|
97
|
-
|
|
120
|
+
statefulLayout.value = /** @type {import('../types.js').VjsfStatefulLayout} */(new StatefulLayout(
|
|
98
121
|
toRaw(compiledLayout.value),
|
|
99
122
|
toRaw(compiledLayout.value.skeletonTrees[compiledLayout.value.mainTree]),
|
|
100
123
|
toRaw(fullOptions.value),
|
|
101
124
|
toRaw(modelValue.value)
|
|
102
125
|
))
|
|
103
|
-
statefulLayout.value = _statefulLayout
|
|
104
|
-
onStatefulLayoutUpdate()
|
|
105
|
-
onDataUpdate()
|
|
106
|
-
_statefulLayout.events.on('update', () => {
|
|
107
|
-
onStatefulLayoutUpdate()
|
|
108
|
-
})
|
|
109
|
-
_statefulLayout.events.on('data', () => {
|
|
110
|
-
onDataUpdate()
|
|
111
|
-
})
|
|
112
|
-
emit('update:state', _statefulLayout)
|
|
113
|
-
_statefulLayout.events.on('autofocus', () => {
|
|
114
|
-
if (!el.value) return
|
|
115
|
-
// @ts-ignore
|
|
116
|
-
const autofocusNodeElement = el.value.querySelector('.vjsf-input--autofocus')
|
|
117
|
-
if (autofocusNodeElement) {
|
|
118
|
-
const autofocusInputElement = autofocusNodeElement.querySelector('input') ?? autofocusNodeElement.querySelector('textarea:not([style*="display: none"]')
|
|
119
|
-
if (autofocusInputElement) autofocusInputElement.focus()
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
// case where options are updated from outside
|
|
125
129
|
watch(fullOptions, (newOptions) => {
|
|
130
|
+
debug('watch fullOptions', fullOptions)
|
|
126
131
|
if (statefulLayout.value) {
|
|
132
|
+
debug(' -> update statefulLayout options')
|
|
127
133
|
statefulLayout.value.options = newOptions
|
|
128
134
|
} else {
|
|
135
|
+
debug(' -> init statefulLayout')
|
|
129
136
|
initStatefulLayout()
|
|
130
137
|
}
|
|
131
138
|
})
|
|
132
139
|
|
|
133
140
|
// case where data is updated from outside
|
|
134
141
|
watch(modelValue, (newData) => {
|
|
135
|
-
|
|
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
|
+
}
|
|
136
148
|
})
|
|
137
149
|
|
|
138
150
|
// case where schema or compile options are updated from outside
|
|
139
151
|
watch(compiledLayout, (newCompiledLayout) => {
|
|
152
|
+
debug('watch compiledLayout', newCompiledLayout)
|
|
153
|
+
debug(' -> init statefulLayout')
|
|
140
154
|
initStatefulLayout()
|
|
141
155
|
})
|
|
142
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
3
|
options: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null;
|
|
4
|
-
modelValue: any;
|
|
5
4
|
schema: Record<string, any>;
|
|
5
|
+
modelValue: any;
|
|
6
6
|
precompiledLayout: import("../../../node_modules/@json-layout/core/types/compile/types.js").CompiledLayout;
|
|
7
7
|
$props: {
|
|
8
8
|
readonly options?: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null | undefined;
|
|
9
|
-
readonly modelValue?: any;
|
|
10
9
|
readonly schema?: Record<string, any> | undefined;
|
|
10
|
+
readonly modelValue?: any;
|
|
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"}
|