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

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.19",
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: 'js-eval' | undefined, expr: string, pure: boolean}}
13
13
  */
14
14
  const fiexEvalExpression = (expression) => {
15
15
  let expr = expression
@@ -4,15 +4,17 @@
4
4
  <v-alert
5
5
  v-show="show"
6
6
  color="info"
7
+ :density="node.options.density"
7
8
  >
8
9
  <div v-html="node.layout.help" />
9
10
  </v-alert>
10
11
  </v-slide-x-reverse-transition>
11
12
  <v-btn
12
13
  color="info"
13
- class="vjsf-help-message-toggle"
14
+ :class="`vjsf-help-message-toggle vjsf-help-message-toggle-${node.options.density}`"
14
15
  :icon="show ? 'mdi-close-circle' : 'mdi-information'"
15
16
  density="compact"
17
+ :size="node.options.density !== 'default' ? 'small' : 'default'"
16
18
  :title="show ? '' : node.messages.showHelp"
17
19
  @click="show = !show"
18
20
  />
@@ -45,5 +47,13 @@ const show = ref(false)
45
47
  right: -4px;
46
48
  z-index: 1;
47
49
  }
50
+ .vjsf-help-message-toggle-comfortable {
51
+ top: -4px;
52
+ right: -4px;
53
+ }
54
+ .vjsf-help-message-toggle-compact {
55
+ top: -4px;
56
+ right: -4px;
57
+ }
48
58
  </style>
49
59
  ../../../types.js
@@ -56,7 +56,7 @@ if (props.modelValue.layout.comp !== 'none' && !props.statefulLayout.options.nod
56
56
  />
57
57
 
58
58
  <help-message
59
- v-if="modelValue.layout.help"
59
+ v-if="modelValue.layout.help && !modelValue.options.summary"
60
60
  :node="modelValue"
61
61
  :class="beforeAfterClasses[modelValue.options.density]"
62
62
  />
@@ -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,10 +57,39 @@ 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(() => (/** @type {import('@json-layout/core').StateNode} */child, /** @type {number} */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>
60
- <v-sheet :elevation="1">
89
+ <v-sheet
90
+ :elevation="2"
91
+ rounded
92
+ >
61
93
  <v-list :density="modelValue.options.density">
62
94
  <v-list-subheader v-if="modelValue.layout.title">
63
95
  {{ modelValue.layout.title }}
@@ -70,7 +102,8 @@ const pushEmptyItem = () => {
70
102
  v-bind="itemBind(childIndex)"
71
103
  :density="modelValue.options.density"
72
104
  :draggable="draggable === childIndex"
73
- :variant="editedItem === childIndex ? 'outlined' : 'flat'"
105
+ variant="flat"
106
+ :style="`border: 1px solid ${itemBorderColor(child, childIndex)}`"
74
107
  class="pa-1 vjsf-list-item"
75
108
  >
76
109
  <v-row class="ma-0">
@@ -82,100 +115,110 @@ const pushEmptyItem = () => {
82
115
  />
83
116
  </v-row>
84
117
  <template
85
- v-if="activeItem === childIndex"
118
+ v-if="!modelValue.options.readOnly && modelValue.layout.listActions.length"
86
119
  #append
87
120
  >
88
121
  <div>
89
- <v-list-item-action
90
- v-if="modelValue.layout.listActions.includes('edit') && modelValue.layout.listEditMode === 'inline-single'"
91
- >
122
+ <v-list-item-action v-if="activeItem !== childIndex">
92
123
  <v-btn
93
- v-if="editedItem !== childIndex"
94
- :title="modelValue.messages.edit"
95
- icon="mdi-pencil"
124
+ style="visibility:hidden"
96
125
  variant="text"
97
- color="primary"
98
126
  :density="buttonDensity"
99
- @click="statefulLayout.activateItem(modelValue, childIndex)"
100
- />
101
- <v-btn
102
- v-else
103
- :title="modelValue.messages.edit"
104
127
  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
128
  />
121
129
  </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}"
130
+ <template v-else>
131
+ <v-list-item-action
132
+ v-if="modelValue.layout.listActions.includes('edit') && modelValue.layout.listEditMode === 'inline-single'"
128
133
  >
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>
134
+ <v-btn
135
+ v-if="editedItem !== childIndex"
136
+ :title="modelValue.messages.edit"
137
+ icon="mdi-pencil"
138
+ variant="text"
139
+ color="primary"
140
+ :density="buttonDensity"
141
+ @click="statefulLayout.activateItem(modelValue, childIndex)"
142
+ />
143
+ <v-btn
144
+ v-else
145
+ :title="modelValue.messages.edit"
146
+ icon="mdi-close"
147
+ variant="flat"
148
+ color="primary"
149
+ :density="buttonDensity"
150
+ @click="statefulLayout.deactivateItem(modelValue)"
151
+ />
152
+ </v-list-item-action>
153
+ <v-list-item-action
154
+ v-if="editedItem === undefined && modelValue.layout.listActions.includes('sort') && activeDnd"
155
+ >
156
+ <v-btn
157
+ :title="modelValue.messages.sort"
158
+ icon="mdi-arrow-up-down"
159
+ variant="plain"
160
+ :density="buttonDensity"
161
+ v-bind="handleBind(childIndex)"
162
+ />
163
+ </v-list-item-action>
164
+ <v-list-item-action
165
+ v-if="editedItem === undefined && (modelValue.layout.listActions.includes('delete') || modelValue.layout.listActions.includes('duplicate') || modelValue.layout.listActions.includes('sort'))"
166
+ >
167
+ <v-menu
168
+ location="bottom end"
169
+ @update:model-value="value => {menuOpened = value ? childIndex : -1}"
170
+ >
171
+ <template #activator="{props: activatorProps}">
172
+ <v-btn
173
+ v-bind="activatorProps"
174
+ icon="mdi-dots-vertical"
175
+ variant="plain"
176
+ slim
177
+ :density="buttonDensity"
178
+ />
179
+ </template>
180
+ <v-list :density="modelValue.options.density">
181
+ <v-list-item
182
+ v-if="modelValue.layout.listActions.includes('delete')"
183
+ base-color="warning"
184
+ @click="deleteItem(childIndex)"
185
+ >
186
+ <template #prepend>
187
+ <v-icon icon="mdi-delete" />
188
+ </template>
189
+ {{ modelValue.messages.delete }}
190
+ </v-list-item>
191
+ <v-list-item
192
+ v-if="modelValue.layout.listActions.includes('duplicate')"
193
+ @click="duplicateItem(child, childIndex)"
194
+ >
195
+ <template #prepend>
196
+ <v-icon icon="mdi-content-duplicate" />
197
+ </template>
198
+ {{ modelValue.messages.duplicate }}
199
+ </v-list-item>
200
+ <v-list-item
201
+ v-if="modelValue.layout.listActions.includes('sort')"
202
+ @click="statefulLayout.input(modelValue, moveArrayItem(modelValue.data, childIndex, childIndex - 1))"
203
+ >
204
+ <template #prepend>
205
+ <v-icon icon="mdi-arrow-up" />
206
+ </template>
207
+ {{ modelValue.messages.up }}
208
+ </v-list-item>
209
+ <v-list-item
210
+ v-if="modelValue.layout.listActions.includes('sort')"
211
+ @click="statefulLayout.input(modelValue, moveArrayItem(modelValue.data, childIndex, childIndex + 1))"
212
+ >
213
+ <template #prepend>
214
+ <v-icon icon="mdi-arrow-down" />
215
+ </template>
216
+ {{ modelValue.messages.down }}
217
+ </v-list-item>
218
+ </v-list>
219
+ </v-menu>
220
+ </v-list-item-action>
221
+ </template>
179
222
  </div>
180
223
  </template>
181
224
  </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"}