@koumoul/vjsf 3.0.0-beta.7 → 3.0.0-beta.9

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.7",
3
+ "version": "3.0.0-beta.9",
4
4
  "description": "Generate forms for the vuetify UI library (vuejs) based on annotated JSON schemas.",
5
5
  "scripts": {
6
6
  "test": "vitest",
@@ -75,7 +75,7 @@
75
75
  "vuetify": "^3.4.9"
76
76
  },
77
77
  "dependencies": {
78
- "@json-layout/core": "0.12.0",
78
+ "@json-layout/core": "0.14.0",
79
79
  "@vueuse/core": "^10.5.0",
80
80
  "debug": "^4.3.4",
81
81
  "ejs": "^3.1.9"
package/src/compat/v2.js CHANGED
@@ -11,6 +11,12 @@ const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
11
11
  /** @type import('@json-layout/vocabulary').PartialCompObject */
12
12
  const layout = {}
13
13
 
14
+ if (schema.separator || schema['x-separator']) {
15
+ layout.separator = schema.separator || schema['x-separator']
16
+ delete schema.separator
17
+ delete schema['x-separator']
18
+ }
19
+
14
20
  if (schema['x-display'] === 'icon' && (schema.enum || schema.items?.enum)) {
15
21
  layout.getItems = { itemIcon: schema['x-itemIcon'] || 'data.value' }
16
22
  delete schema['x-display']
@@ -28,7 +28,7 @@ const fieldProps = computed(() => {
28
28
  })
29
29
 
30
30
  const menuProps = computed(() => {
31
- const menuProps = getCompProps(props.modelValue, 'menu', false)
31
+ const menuProps = getCompProps(props.modelValue)
32
32
  menuProps.closeOnContentClick = false
33
33
  menuProps.disabled = true
34
34
  return menuProps
@@ -18,7 +18,8 @@ const props = defineProps({
18
18
  })
19
19
 
20
20
  const colorPickerProps = computed(() => {
21
- const colorPickerProps = getCompProps(props.modelValue, 'colorPicker', true)
21
+ const colorPickerProps = getCompProps(props.modelValue, true)
22
+ colorPickerProps.modelValue = props.modelValue.data
22
23
  return colorPickerProps
23
24
  })
24
25
  </script>
@@ -21,7 +21,7 @@ const props = defineProps({
21
21
  const vDate = useDate()
22
22
 
23
23
  const datePickerProps = computed(() => {
24
- const datePickerProps = getCompProps(props.modelValue, 'datePicker', true)
24
+ const datePickerProps = getCompProps(props.modelValue, true)
25
25
  datePickerProps.hideActions = true
26
26
  if (props.modelValue.data) datePickerProps.modelValue = new Date(props.modelValue.data)
27
27
  return datePickerProps
@@ -1,8 +1,10 @@
1
1
  <script setup>
2
- import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VContainer, VRow, VIcon } from 'vuetify/components'
2
+ import { VExpansionPanels, VExpansionPanel, VExpansionPanelTitle, VExpansionPanelText, VContainer, VRow, VIcon } from 'vuetify/components'
3
+ import { computed } from 'vue'
3
4
  import { isSection } from '@json-layout/core'
4
5
  import Node from '../node.vue'
5
6
  import SectionHeader from '../fragments/section-header.vue'
7
+ import { getCompProps } from '../../utils/index.js'
6
8
 
7
9
  defineProps({
8
10
  modelValue: {
@@ -21,7 +23,7 @@ defineProps({
21
23
 
22
24
  <template>
23
25
  <section-header :node="modelValue" />
24
- <v-expansion-panels>
26
+ <v-expansion-panels v-bind="getCompProps(modelValue, true)">
25
27
  <v-expansion-panel
26
28
  v-for="(child, i) of modelValue.children"
27
29
  :key="child.key"
@@ -37,16 +39,24 @@ defineProps({
37
39
  </v-icon>
38
40
  {{ child.layout.title ?? child.layout.label }}
39
41
  </v-expansion-panel-title>
40
- <v-container fluid>
41
- <v-row>
42
- <node
43
- v-for="grandChild of isSection(child) ? child.children : [child]"
44
- :key="grandChild.fullKey"
45
- :model-value="/** @type import('../../types.js').VjsfNode */(grandChild)"
46
- :stateful-layout="statefulLayout"
47
- />
48
- </v-row>
49
- </v-container>
42
+ <v-expansion-panel-text>
43
+ <v-container fluid>
44
+ <v-row>
45
+ <node
46
+ v-for="grandChild of isSection(child) ? child.children : [child]"
47
+ :key="grandChild.fullKey"
48
+ :model-value="/** @type import('../../types.js').VjsfNode */(grandChild)"
49
+ :stateful-layout="statefulLayout"
50
+ />
51
+ </v-row>
52
+ </v-container>
53
+ </v-expansion-panel-text>
50
54
  </v-expansion-panel>
51
55
  </v-expansion-panels>
52
56
  </template>
57
+
58
+ <style>
59
+ .vjsf-node-expansion-panels .v-expansion-panel-text__wrapper {
60
+ padding: 0;
61
+ }
62
+ </style>
@@ -48,6 +48,12 @@ const buttonDensity = computed(() => {
48
48
  return props.modelValue.options.density
49
49
  })
50
50
 
51
+ const pushEmptyItem = () => {
52
+ const newData = (props.modelValue.data ?? []).concat([undefined])
53
+ props.statefulLayout.input(props.modelValue, newData)
54
+ props.statefulLayout.activateItem(props.modelValue, newData.length - 1)
55
+ }
56
+
51
57
  </script>
52
58
 
53
59
  <template>
@@ -180,7 +186,7 @@ const buttonDensity = computed(() => {
180
186
  <v-btn
181
187
  color="primary"
182
188
  :density="modelValue.options.density"
183
- @click="statefulLayout.input(modelValue, (modelValue.data ?? []).concat([undefined]))"
189
+ @click="pushEmptyItem"
184
190
  >
185
191
  {{ modelValue.messages.addItem }}
186
192
  </v-btn>
@@ -1,5 +1,5 @@
1
1
  import { StatefulLayout, produceCompileOptions } from '@json-layout/core'
2
- import { inject, shallowRef, computed, ref, watch, useSlots } from 'vue'
2
+ 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 { registeredNodeComponents } from '../utils/index.js'
@@ -30,6 +30,8 @@ export const emits = {
30
30
  */
31
31
  export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compile, precompiledLayout) => {
32
32
  const el = ref(null)
33
+
34
+ // TODO: apply a debounce to width ?
33
35
  const { width } = useElementSize(el)
34
36
 
35
37
  /** @type import('vue').ShallowRef<import('../types.js').VjsfStatefulLayout | null> */
@@ -53,7 +55,7 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
53
55
 
54
56
  const slots = useSlots()
55
57
 
56
- const fullOptions = computed(() => getFullOptions(options.value, form, width.value, slots, { ...nodeComponents, ...registeredNodeComponents.value }))
58
+ const fullOptions = computed(() => getFullOptions(options.value, form, width.value, slots, { ...nodeComponents, ...toRaw(registeredNodeComponents.value) }))
57
59
 
58
60
  // do not use a simple computed here as we want to prevent recompiling the layout when the options are the same
59
61
  /** @type {import('vue').Ref<import('@json-layout/core').PartialCompileOptions>} */
@@ -74,7 +76,6 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
74
76
  const onStatefulLayoutUpdate = () => {
75
77
  if (!statefulLayout.value) return
76
78
  stateTree.value = statefulLayout.value.stateTree
77
- emit('update:modelValue', statefulLayout.value.data)
78
79
  emit('update:state', statefulLayout.value)
79
80
  if (form) {
80
81
  // cf https://vuetifyjs.com/en/components/forms/#validation-state
@@ -84,21 +85,31 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
84
85
  }
85
86
  }
86
87
 
88
+ const onDataUpdate = () => {
89
+ if (statefulLayout.value && modelValue !== statefulLayout.value.data) {
90
+ emit('update:modelValue', statefulLayout.value.data)
91
+ }
92
+ }
93
+
87
94
  const initStatefulLayout = () => {
88
95
  if (!width.value) return
89
96
 
90
97
  // @ts-ignore
91
98
  const _statefulLayout = /** @type {import('../types.js').VjsfStatefulLayout} */(new StatefulLayout(
92
- compiledLayout.value,
93
- compiledLayout.value.skeletonTree,
94
- fullOptions.value,
95
- modelValue.value
99
+ toRaw(compiledLayout.value),
100
+ toRaw(compiledLayout.value.skeletonTree),
101
+ toRaw(fullOptions.value),
102
+ toRaw(modelValue.value)
96
103
  ))
97
104
  statefulLayout.value = _statefulLayout
98
105
  onStatefulLayoutUpdate()
106
+ onDataUpdate()
99
107
  _statefulLayout.events.on('update', () => {
100
108
  onStatefulLayoutUpdate()
101
109
  })
110
+ _statefulLayout.events.on('data', () => {
111
+ onDataUpdate()
112
+ })
102
113
  emit('update:state', _statefulLayout)
103
114
  _statefulLayout.events.on('autofocus', () => {
104
115
  if (!el.value) return
@@ -122,7 +133,7 @@ export const useVjsf = (schema, modelValue, options, nodeComponents, emit, compi
122
133
 
123
134
  // case where data is updated from outside
124
135
  watch(modelValue, (newData) => {
125
- if (statefulLayout.value && statefulLayout.value.data !== newData) statefulLayout.value.data = newData
136
+ if (statefulLayout.value && statefulLayout.value.data !== newData) statefulLayout.value.data = toRaw(newData)
126
137
  })
127
138
 
128
139
  // case where schema or compile options are updated from outside
@@ -1 +1 @@
1
- export { commonjsDeps } from '@json-layout/core/utils/build'
1
+ export { commonjsDeps, commonjsDepsPaths } from '@json-layout/core/utils/build'
@@ -65,7 +65,7 @@ export function getInputProps (node, statefulLayout, layoutPropsMap, isMainComp
65
65
  if (node.error && node.validated) {
66
66
  fullProps.errorMessages = node.error
67
67
  }
68
- fullProps.modelValue = node.data
68
+ fullProps.modelValue = (typeof node.data === 'string' && node.layout.separator) ? node.data.split(/** @type {string} */(node.layout.separator)) : node.data
69
69
  if (node.options.readOnly) {
70
70
  fullProps.disabled = true
71
71
  fullProps.class.push('vjsf-input--readonly')
@@ -82,7 +82,9 @@ export function getInputProps (node, statefulLayout, layoutPropsMap, isMainComp
82
82
  }
83
83
 
84
84
  if (isMainComp) {
85
- fullProps['onUpdate:modelValue'] = (/** @type string */value) => statefulLayout.input(node, value)
85
+ fullProps['onUpdate:modelValue'] = (/** @type string */value) => {
86
+ return statefulLayout.input(node, (Array.isArray(value) && node.layout.separator) ? value.join(/** @type {string} */(node.layout.separator)) : value)
87
+ }
86
88
  fullProps.onBlur = () => statefulLayout.blur(node)
87
89
  }
88
90
 
@@ -92,18 +94,14 @@ export function getInputProps (node, statefulLayout, layoutPropsMap, isMainComp
92
94
  // calculate the props of components that are not of the field category
93
95
  /**
94
96
  * @param {import('@json-layout/core').StateNode} node
95
- * @param {string} comp
96
97
  * @param {boolean} isMainComp
97
98
  * @returns {Record<string, any>}
98
99
  */
99
- export function getCompProps (node, comp, isMainComp = true) {
100
+ export function getCompProps (node, isMainComp = true) {
100
101
  const options = /** @type import('../types.js').VjsfOptions */(node.options)
101
102
  /** @type {(Record<string, any> | undefined)[]} */
102
103
  const propsLevels = [{ density: options.density }]
103
- propsLevels.push(/** @type Record<string, any> | undefined */(options[`${comp}Props`]))
104
- if (node.options.readOnly) propsLevels.push(/** @type Record<string, any> | undefined */(options[`${comp}PropsReadOnly`]))
105
104
  if (isMainComp) propsLevels.push(node.layout.props)
106
105
  const fullProps = mergePropsLevels(propsLevels)
107
- if (isMainComp) fullProps.modelValue = node.data
108
106
  return fullProps
109
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../src/compat/v2.js"],"names":[],"mappings":"AA8FA;;;;;;GAMG;AACH,kCALW,MAAM,+CAEN,MAAM,0BAkBhB;sBApHqB,KAAK"}
1
+ {"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../src/compat/v2.js"],"names":[],"mappings":"AAoGA;;;;;;GAMG;AACH,kCALW,MAAM,+CAEN,MAAM,0BAkBhB;sBA1HqB,KAAK"}
@@ -1,5 +1,5 @@
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">> | null;
4
4
  modelValue: any;
5
5
  schema: Record<string, any>;
@@ -1 +1 @@
1
- {"version":3,"file":"use-vjsf.d.ts","sourceRoot":"","sources":["../../src/composables/use-vjsf.js"],"names":[],"mappings":"AAUA;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;;;;EA2Gb"}
1
+ {"version":3,"file":"use-vjsf.d.ts","sourceRoot":"","sources":["../../src/composables/use-vjsf.js"],"names":[],"mappings":"AAUA;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,2 +1,2 @@
1
- export { commonjsDeps } from "@json-layout/core/utils/build";
1
+ export { commonjsDeps, commonjsDepsPaths } from "@json-layout/core/utils/build";
2
2
  //# sourceMappingURL=build.d.ts.map
@@ -15,9 +15,8 @@ export function mergePropsLevels(propsLevels: (Record<string, any> | undefined)[
15
15
  export function getInputProps(node: import('../types.js').VjsfNode, statefulLayout: import('../types.js').VjsfStatefulLayout, layoutPropsMap?: (string | [string, string])[] | undefined, isMainComp?: boolean | undefined): Record<string, any>;
16
16
  /**
17
17
  * @param {import('@json-layout/core').StateNode} node
18
- * @param {string} comp
19
18
  * @param {boolean} isMainComp
20
19
  * @returns {Record<string, any>}
21
20
  */
22
- export function getCompProps(node: import('@json-layout/core').StateNode, comp: string, isMainComp?: boolean): Record<string, any>;
21
+ export function getCompProps(node: import('@json-layout/core').StateNode, isMainComp?: boolean): Record<string, any>;
23
22
  //# sourceMappingURL=props.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../../src/utils/props.js"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,8CAHW,CAAC,OAAO,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,EAAE,GACjC,OAAO,MAAM,EAAE,GAAG,CAAC,GAAG;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAC,CAqBnD;AAID;;;;;;GAMG;AACH,oCANW,OAAO,aAAa,EAAE,QAAQ,kBAC9B,OAAO,aAAa,EAAE,kBAAkB,iGAGtC,OAAO,MAAM,EAAE,GAAG,CAAC,CAuC/B;AAGD;;;;;GAKG;AACH,mCALW,OAAO,mBAAmB,EAAE,SAAS,QACrC,MAAM,eACN,OAAO,GACL,OAAO,MAAM,EAAE,GAAG,CAAC,CAY/B"}
1
+ {"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../../src/utils/props.js"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,8CAHW,CAAC,OAAO,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,EAAE,GACjC,OAAO,MAAM,EAAE,GAAG,CAAC,GAAG;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAC,CAqBnD;AAID;;;;;;GAMG;AACH,oCANW,OAAO,aAAa,EAAE,QAAQ,kBAC9B,OAAO,aAAa,EAAE,kBAAkB,iGAGtC,OAAO,MAAM,EAAE,GAAG,CAAC,CAyC/B;AAGD;;;;GAIG;AACH,mCAJW,OAAO,mBAAmB,EAAE,SAAS,eACrC,OAAO,GACL,OAAO,MAAM,EAAE,GAAG,CAAC,CAS/B"}