@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.26",
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.22.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
- const fullOptions = computed(() => getFullOptions(options.value, form, width.value, slots, { ...nodeComponents }))
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) compileOptions.value = newCompileOptions
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
- const _statefulLayout = /** @type {import('../types.js').VjsfStatefulLayout} */(new StatefulLayout(
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
- if (statefulLayout.value && statefulLayout.value.data !== newData) statefulLayout.value.data = toRaw(newData)
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>): import("../types.js").VjsfOptions;
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;AAWM,wCAPI,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,qCAwBjD"}
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:modelValue", data: any) => void) & ((event: "update:state", state: import("../types.js").VjsfStatefulLayout) => void);
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":"AASA;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;;;;EAsHb"}
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"}