@koumoul/vjsf 3.0.0-beta.17 → 3.0.0-beta.18

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.17",
3
+ "version": "3.0.0-beta.18",
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.16.3",
78
+ "@json-layout/core": "0.16.4",
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
@@ -9,7 +9,7 @@ const Ajv = /** @type {typeof ajvModule.default} */ (ajvModule)
9
9
  /**
10
10
  *
11
11
  * @param {string} expression
12
- * @returns {{expr: string, pure: boolean}}
12
+ * @returns {{type: string, expr: string, pure: boolean}}
13
13
  */
14
14
  const fiexEvalExpression = (expression) => {
15
15
  let expr = expression
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import { watch, computed, ref } from 'vue'
3
+ import { useTheme } from 'vuetify'
3
4
  import { VList, VListItem, VListItemAction, VBtn, VMenu, VIcon, VSheet, VSpacer, VDivider, VRow, VListSubheader } from 'vuetify/components'
4
5
  import { isSection, clone } from '@json-layout/core'
5
6
  import Node from '../node.vue'
@@ -19,6 +20,8 @@ const props = defineProps({
19
20
  }
20
21
  })
21
22
 
23
+ const theme = useTheme()
24
+
22
25
  /* use composable for drag and drop */
23
26
  const { activeDnd, sortableArray, draggable, hovered, dragging, itemBind, handleBind } = useDnd(props.modelValue.children, () => {
24
27
  props.statefulLayout.input(props.modelValue, sortableArray.value.map((child) => child.data))
@@ -54,6 +57,32 @@ const pushEmptyItem = () => {
54
57
  props.statefulLayout.activateItem(props.modelValue, newData.length - 1)
55
58
  }
56
59
 
60
+ /**
61
+ * @param {number} childIndex
62
+ */
63
+ const deleteItem = (childIndex) => {
64
+ const newData = [...props.modelValue.data.slice(0, childIndex), ...props.modelValue.data.slice(childIndex + 1)]
65
+ props.statefulLayout.input(props.modelValue, newData)
66
+ menuOpened.value = -1
67
+ }
68
+
69
+ /**
70
+ * @param {import('@json-layout/core').StateNode} child
71
+ * @param {number} childIndex
72
+ */
73
+ const duplicateItem = (child, childIndex) => {
74
+ const newData = [...props.modelValue.data.slice(0, childIndex), clone(child.data), ...props.modelValue.data.slice(childIndex)]
75
+ props.statefulLayout.input(props.modelValue, newData)
76
+ props.statefulLayout.activateItem(props.modelValue, childIndex + 1)
77
+ menuOpened.value = -1
78
+ }
79
+
80
+ const itemBorderColor = computed(() => (child, childIndex) => {
81
+ if (editedItem.value === childIndex) return theme.current.value.colors.primary
82
+ if (child.validated && (child.error || child.childError)) return theme.current.value.colors.error
83
+ return 'transparent'
84
+ })
85
+
57
86
  </script>
58
87
 
59
88
  <template>
@@ -70,7 +99,8 @@ const pushEmptyItem = () => {
70
99
  v-bind="itemBind(childIndex)"
71
100
  :density="modelValue.options.density"
72
101
  :draggable="draggable === childIndex"
73
- :variant="editedItem === childIndex ? 'outlined' : 'flat'"
102
+ variant="flat"
103
+ :style="`border: 1px solid ${itemBorderColor(child, childIndex)}`"
74
104
  class="pa-1 vjsf-list-item"
75
105
  >
76
106
  <v-row class="ma-0">
@@ -82,100 +112,110 @@ const pushEmptyItem = () => {
82
112
  />
83
113
  </v-row>
84
114
  <template
85
- v-if="activeItem === childIndex"
115
+ v-if="!modelValue.options.readOnly && modelValue.layout.listActions.length"
86
116
  #append
87
117
  >
88
118
  <div>
89
- <v-list-item-action
90
- v-if="modelValue.layout.listActions.includes('edit') && modelValue.layout.listEditMode === 'inline-single'"
91
- >
119
+ <v-list-item-action v-if="activeItem !== childIndex">
92
120
  <v-btn
93
- v-if="editedItem !== childIndex"
94
- :title="modelValue.messages.edit"
95
- icon="mdi-pencil"
121
+ style="visibility:hidden"
96
122
  variant="text"
97
- color="primary"
98
123
  :density="buttonDensity"
99
- @click="statefulLayout.activateItem(modelValue, childIndex)"
100
- />
101
- <v-btn
102
- v-else
103
- :title="modelValue.messages.edit"
104
124
  icon="mdi-pencil"
105
- variant="flat"
106
- color="primary"
107
- :density="buttonDensity"
108
- @click="statefulLayout.deactivateItem(modelValue)"
109
- />
110
- </v-list-item-action>
111
- <v-list-item-action
112
- v-if="editedItem === undefined && modelValue.layout.listActions.includes('sort') && activeDnd"
113
- >
114
- <v-btn
115
- :title="modelValue.messages.sort"
116
- icon="mdi-arrow-up-down"
117
- variant="plain"
118
- :density="buttonDensity"
119
- v-bind="handleBind(childIndex)"
120
125
  />
121
126
  </v-list-item-action>
122
- <v-list-item-action
123
- v-if="editedItem === undefined && (modelValue.layout.listActions.includes('delete') || modelValue.layout.listActions.includes('duplicate') || modelValue.layout.listActions.includes('sort'))"
124
- >
125
- <v-menu
126
- location="bottom end"
127
- @update:model-value="value => {menuOpened = value ? childIndex : -1}"
127
+ <template v-else>
128
+ <v-list-item-action
129
+ v-if="modelValue.layout.listActions.includes('edit') && modelValue.layout.listEditMode === 'inline-single'"
128
130
  >
129
- <template #activator="{props: activatorProps}">
130
- <v-btn
131
- v-bind="activatorProps"
132
- icon="mdi-dots-vertical"
133
- variant="plain"
134
- slim
135
- :density="buttonDensity"
136
- />
137
- </template>
138
- <v-list :density="modelValue.options.density">
139
- <v-list-item
140
- v-if="modelValue.layout.listActions.includes('delete')"
141
- base-color="warning"
142
- @click="statefulLayout.input(modelValue, [...modelValue.data.slice(0, childIndex), ...modelValue.data.slice(childIndex + 1)])"
143
- >
144
- <template #prepend>
145
- <v-icon icon="mdi-delete" />
146
- </template>
147
- {{ modelValue.messages.delete }}
148
- </v-list-item>
149
- <v-list-item
150
- v-if="modelValue.layout.listActions.includes('duplicate')"
151
- @click="statefulLayout.input(modelValue, [...modelValue.data.slice(0, childIndex), clone(child.data), ...modelValue.data.slice(childIndex)])"
152
- >
153
- <template #prepend>
154
- <v-icon icon="mdi-content-duplicate" />
155
- </template>
156
- {{ modelValue.messages.duplicate }}
157
- </v-list-item>
158
- <v-list-item
159
- v-if="modelValue.layout.listActions.includes('sort')"
160
- @click="statefulLayout.input(modelValue, moveArrayItem(modelValue.data, childIndex, childIndex - 1))"
161
- >
162
- <template #prepend>
163
- <v-icon icon="mdi-arrow-up" />
164
- </template>
165
- {{ modelValue.messages.up }}
166
- </v-list-item>
167
- <v-list-item
168
- v-if="modelValue.layout.listActions.includes('sort')"
169
- @click="statefulLayout.input(modelValue, moveArrayItem(modelValue.data, childIndex, childIndex + 1))"
170
- >
171
- <template #prepend>
172
- <v-icon icon="mdi-arrow-down" />
173
- </template>
174
- {{ modelValue.messages.down }}
175
- </v-list-item>
176
- </v-list>
177
- </v-menu>
178
- </v-list-item-action>
131
+ <v-btn
132
+ v-if="editedItem !== childIndex"
133
+ :title="modelValue.messages.edit"
134
+ icon="mdi-pencil"
135
+ variant="text"
136
+ color="primary"
137
+ :density="buttonDensity"
138
+ @click="statefulLayout.activateItem(modelValue, childIndex)"
139
+ />
140
+ <v-btn
141
+ v-else
142
+ :title="modelValue.messages.edit"
143
+ icon="mdi-close"
144
+ variant="flat"
145
+ color="primary"
146
+ :density="buttonDensity"
147
+ @click="statefulLayout.deactivateItem(modelValue)"
148
+ />
149
+ </v-list-item-action>
150
+ <v-list-item-action
151
+ v-if="editedItem === undefined && modelValue.layout.listActions.includes('sort') && activeDnd"
152
+ >
153
+ <v-btn
154
+ :title="modelValue.messages.sort"
155
+ icon="mdi-arrow-up-down"
156
+ variant="plain"
157
+ :density="buttonDensity"
158
+ v-bind="handleBind(childIndex)"
159
+ />
160
+ </v-list-item-action>
161
+ <v-list-item-action
162
+ v-if="editedItem === undefined && (modelValue.layout.listActions.includes('delete') || modelValue.layout.listActions.includes('duplicate') || modelValue.layout.listActions.includes('sort'))"
163
+ >
164
+ <v-menu
165
+ location="bottom end"
166
+ @update:model-value="value => {menuOpened = value ? childIndex : -1}"
167
+ >
168
+ <template #activator="{props: activatorProps}">
169
+ <v-btn
170
+ v-bind="activatorProps"
171
+ icon="mdi-dots-vertical"
172
+ variant="plain"
173
+ slim
174
+ :density="buttonDensity"
175
+ />
176
+ </template>
177
+ <v-list :density="modelValue.options.density">
178
+ <v-list-item
179
+ v-if="modelValue.layout.listActions.includes('delete')"
180
+ base-color="warning"
181
+ @click="deleteItem(childIndex)"
182
+ >
183
+ <template #prepend>
184
+ <v-icon icon="mdi-delete" />
185
+ </template>
186
+ {{ modelValue.messages.delete }}
187
+ </v-list-item>
188
+ <v-list-item
189
+ v-if="modelValue.layout.listActions.includes('duplicate')"
190
+ @click="duplicateItem(child, childIndex)"
191
+ >
192
+ <template #prepend>
193
+ <v-icon icon="mdi-content-duplicate" />
194
+ </template>
195
+ {{ modelValue.messages.duplicate }}
196
+ </v-list-item>
197
+ <v-list-item
198
+ v-if="modelValue.layout.listActions.includes('sort')"
199
+ @click="statefulLayout.input(modelValue, moveArrayItem(modelValue.data, childIndex, childIndex - 1))"
200
+ >
201
+ <template #prepend>
202
+ <v-icon icon="mdi-arrow-up" />
203
+ </template>
204
+ {{ modelValue.messages.up }}
205
+ </v-list-item>
206
+ <v-list-item
207
+ v-if="modelValue.layout.listActions.includes('sort')"
208
+ @click="statefulLayout.input(modelValue, moveArrayItem(modelValue.data, childIndex, childIndex + 1))"
209
+ >
210
+ <template #prepend>
211
+ <v-icon icon="mdi-arrow-down" />
212
+ </template>
213
+ {{ modelValue.messages.down }}
214
+ </v-list-item>
215
+ </v-list>
216
+ </v-menu>
217
+ </v-list-item-action>
218
+ </template>
179
219
  </div>
180
220
  </template>
181
221
  </v-list-item>
@@ -1,7 +1,8 @@
1
1
  <script setup>
2
2
  import { VSelect, VRow } from 'vuetify/components'
3
- import { shallowRef, watch } from 'vue'
3
+ import { shallowRef, watch, computed, h } from 'vue'
4
4
  import { isSection } from '@json-layout/core'
5
+ import { getInputProps, getCompSlots } from '../../utils/index.js'
5
6
  import Node from '../node.vue'
6
7
 
7
8
  const props = defineProps({
@@ -33,19 +34,21 @@ const onChange = (/** @type import('@json-layout/core').SkeletonTree */childTree
33
34
  props.statefulLayout.activateItem(props.modelValue, props.modelValue.skeleton.childrenTrees.indexOf(childTree))
34
35
  }
35
36
 
37
+ const fieldProps = computed(() => {
38
+ const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
39
+ fieldProps.modelValue = activeChildTree.value
40
+ fieldProps['onUpdate:modelValue'] = onChange
41
+ fieldProps.returnObject = true
42
+ fieldProps.items = props.modelValue.skeleton.childrenTrees
43
+ fieldProps.itemTitle = 'title'
44
+ return fieldProps
45
+ })
36
46
  </script>
37
47
 
38
48
  <template>
39
49
  <v-select
40
50
  v-if="modelValue.skeleton.childrenTrees"
41
- v-model="activeChildTree"
42
- :items="modelValue.skeleton.childrenTrees"
43
- item-title="title"
44
- return-object
45
- :label="modelValue.layout.label"
46
- :error-messages="modelValue.validated ? modelValue.error : null"
47
- :density="modelValue.options.density"
48
- @update:model-value="onChange"
51
+ v-bind="fieldProps"
49
52
  />
50
53
  <v-row v-if="modelValue.children?.[0]">
51
54
  <node
@@ -42,6 +42,7 @@ export default function useDnd (array, callback) {
42
42
  dragging.value = itemIndex
43
43
  },
44
44
  onDragend: () => {
45
+ hovered.value = itemIndex
45
46
  dragging.value = -1
46
47
  callback()
47
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../src/compat/v2.js"],"names":[],"mappings":"AAqHA;;;;;;GAMG;AACH,kCALW,MAAM,+CAEN,MAAM,0BAkBhB;sBA3IqB,KAAK"}
1
+ {"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../src/compat/v2.js"],"names":[],"mappings":"AAwIA;;;;;;GAMG;AACH,kCALW,MAAM,+CAEN,MAAM,0BAkBhB;sBA9JqB,KAAK"}
@@ -1,12 +1,12 @@
1
1
  declare const _default: import("vue").DefineComponent<{}, {
2
2
  $emit: ((event: "update:modelValue", data: any) => void) & ((event: "update:state", state: import("../types.js").VjsfStatefulLayout) => void);
3
- options: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null;
4
3
  modelValue: any;
4
+ options: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null;
5
5
  schema: Record<string, any>;
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;
9
8
  readonly modelValue?: any;
9
+ readonly options?: Partial<Omit<import("../types.js").VjsfOptions, "width" | "vjsfSlots">> | null | undefined;
10
10
  readonly schema?: Record<string, any> | undefined;
11
11
  readonly precompiledLayout?: import("../../../node_modules/@json-layout/core/types/compile/types.js").CompiledLayout | undefined;
12
12
  };
@@ -1 +1 @@
1
- {"version":3,"file":"use-dnd.d.ts","sourceRoot":"","sources":["../../src/composables/use-dnd.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wDAHW,MAAM,IAAI;;;;;;0BAoBU,MAAM;;;;;;;4BAuBJ,MAAM;;;;EAmBtC"}
1
+ {"version":3,"file":"use-dnd.d.ts","sourceRoot":"","sources":["../../src/composables/use-dnd.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wDAHW,MAAM,IAAI;;;;;;0BAoBU,MAAM;;;;;;;4BAwBJ,MAAM;;;;EAmBtC"}