@koumoul/vjsf 3.0.0-beta.23 → 3.0.0-beta.24
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/README.md +21 -0
- package/package.json +4 -3
- package/src/compat/v2.js +44 -13
- package/src/components/fragments/select-item-icon.vue +1 -1
- package/src/components/fragments/select-selection.vue +2 -1
- package/src/components/fragments/selection-group.vue +5 -25
- package/src/components/fragments/text-field-menu.vue +4 -0
- package/src/components/nodes/autocomplete.vue +9 -35
- package/src/components/nodes/list.vue +1 -1
- package/src/components/nodes/radio-group.vue +5 -25
- package/src/components/nodes/select.vue +7 -27
- package/src/composables/use-get-items.js +48 -0
- package/types/compat/v2.d.ts.map +1 -1
- package/types/components/fragments/select-item.vue.d.ts +2 -2
- package/types/components/fragments/selection-group.vue.d.ts.map +1 -1
- package/types/components/fragments/text-field-menu.vue.d.ts.map +1 -1
- package/types/components/nodes/autocomplete.vue.d.ts.map +1 -1
- package/types/components/nodes/radio-group.vue.d.ts.map +1 -1
- package/types/components/nodes/select.vue.d.ts.map +1 -1
- package/types/components/vjsf.vue.d.ts +2 -2
- package/types/composables/use-get-items.d.ts +13 -0
- package/types/composables/use-get-items.d.ts.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# VJSF
|
|
2
|
+
|
|
3
|
+
*vuetify-json-schema-form* - *@koumoul/vjsf on npm*
|
|
4
|
+
|
|
5
|
+
Easily create beautiful forms that output valid data.
|
|
6
|
+
|
|
7
|
+
Based on [Vue.js](https://vuejs.org/) / [Vuetify](https://vuetifyjs.com/) / [JSON Schema](https://json-schema.org/) / [JSON Layout](https://github.com/json-layout/json-layout).
|
|
8
|
+
|
|
9
|
+
See [the documentation](https://koumoul-dev.github.io/vuetify-jsonschema-form/latest/).
|
|
10
|
+
|
|
11
|
+
See [the documentation for deprecated v2](https://koumoul-dev.github.io/vuetify-jsonschema-form/2.x/).
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
## Bug reports
|
|
16
|
+
|
|
17
|
+
Bug reports are created using github issues. The examples in the documentation include codepen links, as much as possible please save a duplicate codepen with the minimal schema/config to reproduce your problem.
|
|
18
|
+
|
|
19
|
+
## Contribute
|
|
20
|
+
|
|
21
|
+
See [CONTRIBUTE.md](./CONTRIBUTE.md).
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koumoul/vjsf",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.24",
|
|
4
4
|
"description": "Generate forms for the vuetify UI library (vuejs) based on annotated JSON schemas.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "vitest",
|
|
7
7
|
"build": "vue-tsc",
|
|
8
|
-
"watch:build": "vue-tsc --watch"
|
|
8
|
+
"watch:build": "vue-tsc --watch",
|
|
9
|
+
"prepublishOnly": "cp ../README.md README.md && cp ../LICENSE LICENSE"
|
|
9
10
|
},
|
|
10
11
|
"author": "Alban Mouton <alban.mouton@gmail.com>",
|
|
11
12
|
"license": "MIT",
|
|
@@ -75,7 +76,7 @@
|
|
|
75
76
|
"vuetify": "^3.6.8"
|
|
76
77
|
},
|
|
77
78
|
"dependencies": {
|
|
78
|
-
"@json-layout/core": "0.
|
|
79
|
+
"@json-layout/core": "0.21.0",
|
|
79
80
|
"@vueuse/core": "^10.5.0",
|
|
80
81
|
"debug": "^4.3.4",
|
|
81
82
|
"ejs": "^3.1.9"
|
package/src/compat/v2.js
CHANGED
|
@@ -6,23 +6,57 @@ import { isPartialGetItemsObj } from '@json-layout/vocabulary'
|
|
|
6
6
|
// @ts-ignore
|
|
7
7
|
const Ajv = /** @type {typeof ajvModule.default} */ (ajvModule)
|
|
8
8
|
|
|
9
|
+
const expressionKeyMappings = {
|
|
10
|
+
modelRoot: 'rootData',
|
|
11
|
+
root: 'rootData',
|
|
12
|
+
model: 'data',
|
|
13
|
+
value: 'data'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} expression
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
const prefixExpression = (expression) => {
|
|
21
|
+
// looks like a simple expression missing the data. prefix
|
|
22
|
+
if (expression.match(/^[a-z.]*$/i) && !['data', 'context', 'rootData', 'parent'].some(key => expression.startsWith(key + '.'))) {
|
|
23
|
+
return 'rootData.' + expression
|
|
24
|
+
}
|
|
25
|
+
return expression
|
|
26
|
+
}
|
|
27
|
+
|
|
9
28
|
/**
|
|
10
29
|
*
|
|
11
30
|
* @param {string} expression
|
|
12
|
-
* @
|
|
31
|
+
* @param {'js-eval' | 'js-tpl'} [type]
|
|
32
|
+
* @returns {{type: 'js-eval' | 'js-tpl', expr: string, pure: boolean}}
|
|
13
33
|
*/
|
|
14
|
-
const
|
|
34
|
+
const fixExpression = (expression, type = 'js-eval') => {
|
|
15
35
|
let expr = expression
|
|
16
36
|
let pure = true
|
|
17
|
-
|
|
18
|
-
pure = false
|
|
19
|
-
}
|
|
37
|
+
|
|
20
38
|
if (expr.includes('parent.value')) {
|
|
21
39
|
pure = false
|
|
22
40
|
expr = expr.replace(/parent\.value/g, 'parent.data')
|
|
23
41
|
}
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
for (const [key, value] of Object.entries(expressionKeyMappings)) {
|
|
44
|
+
expr = expr.replace(new RegExp(`${key}\\.`, 'g'), value + '.')
|
|
45
|
+
}
|
|
46
|
+
if (type === 'js-eval') {
|
|
47
|
+
expr = prefixExpression(expr)
|
|
48
|
+
}
|
|
49
|
+
if (type === 'js-tpl') {
|
|
50
|
+
for (const expressionMatch of expr.matchAll(/\{(.*?)\}/g)) {
|
|
51
|
+
if (expressionMatch[1] !== 'q') expr = expr.replace(expressionMatch[0], '${' + prefixExpression(expressionMatch[1]) + '}')
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (expr.includes('rootData')) {
|
|
56
|
+
pure = false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { type, expr, pure }
|
|
26
60
|
}
|
|
27
61
|
|
|
28
62
|
const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
|
|
@@ -62,22 +96,19 @@ const processFragment = (/** @type {import("ajv").SchemaObject} */schema) => {
|
|
|
62
96
|
|
|
63
97
|
if (schema['x-fromData']) {
|
|
64
98
|
layout.comp = layout.comp ?? 'select'
|
|
65
|
-
layout.getItems =
|
|
99
|
+
layout.getItems = fixExpression(schema['x-fromData'])
|
|
66
100
|
delete schema['x-fromData']
|
|
67
101
|
}
|
|
68
102
|
|
|
69
103
|
if (schema['x-if']) {
|
|
70
|
-
layout.if =
|
|
104
|
+
layout.if = fixExpression(schema['x-if'])
|
|
71
105
|
delete schema['x-if']
|
|
72
106
|
}
|
|
73
107
|
|
|
74
108
|
if (schema['x-fromUrl']) {
|
|
75
109
|
/** @type string */
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (expressionMatch[1] !== 'q') url = url.replace(expressionMatch[0], '$' + expressionMatch[0])
|
|
79
|
-
}
|
|
80
|
-
layout.getItems = { url }
|
|
110
|
+
const url = schema['x-fromUrl']
|
|
111
|
+
layout.getItems = { url: fixExpression(url, 'js-tpl') }
|
|
81
112
|
delete schema['x-fromUrl']
|
|
82
113
|
}
|
|
83
114
|
if (layout.getItems && isPartialGetItemsObj(layout.getItems)) {
|
|
@@ -20,7 +20,7 @@ export default defineComponent({
|
|
|
20
20
|
} else if (isSVG.value) {
|
|
21
21
|
return h('div', { innerHTML: props.icon.replace('<svg ', '<svg class="v-icon__svg" '), class: 'v-icon' })
|
|
22
22
|
} else {
|
|
23
|
-
return h(VIcon, null, props.icon)
|
|
23
|
+
return h(VIcon, null, () => props.icon)
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -18,6 +18,7 @@ defineProps({
|
|
|
18
18
|
required: true
|
|
19
19
|
}
|
|
20
20
|
})
|
|
21
|
+
|
|
21
22
|
</script>
|
|
22
23
|
|
|
23
24
|
<template>
|
|
@@ -26,7 +27,7 @@ defineProps({
|
|
|
26
27
|
v-if="item.icon"
|
|
27
28
|
:icon="item.icon"
|
|
28
29
|
/>
|
|
29
|
-
{{ item.title }}
|
|
30
|
+
{{ item.title ?? item.key ?? item.value }}
|
|
30
31
|
<span
|
|
31
32
|
v-if="multiple && !last"
|
|
32
33
|
class="v-select__selection-comma"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { VInput, VLabel, VCheckbox, VSwitch, VSkeletonLoader } from 'vuetify/components'
|
|
3
|
-
import { defineComponent, h, computed
|
|
3
|
+
import { defineComponent, h, computed } from 'vue'
|
|
4
4
|
import { getInputProps, getCompSlots } from '../../utils/index.js'
|
|
5
|
+
import useGetItems from '../../composables/use-get-items.js'
|
|
5
6
|
|
|
6
7
|
export default defineComponent({
|
|
7
8
|
props: {
|
|
@@ -21,10 +22,7 @@ export default defineComponent({
|
|
|
21
22
|
}
|
|
22
23
|
},
|
|
23
24
|
setup (props) {
|
|
24
|
-
|
|
25
|
-
const items = shallowRef([])
|
|
26
|
-
/** @type import('vue').Ref<boolean> */
|
|
27
|
-
const loading = ref(false)
|
|
25
|
+
const getItems = useGetItems(props)
|
|
28
26
|
|
|
29
27
|
const fieldProps = computed(() => {
|
|
30
28
|
const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
|
|
@@ -33,24 +31,6 @@ export default defineComponent({
|
|
|
33
31
|
return fieldProps
|
|
34
32
|
})
|
|
35
33
|
|
|
36
|
-
/** @type import('@json-layout/core').StateTree | null */
|
|
37
|
-
let lastStateTree = null
|
|
38
|
-
/** @type Record<string, any> | null */
|
|
39
|
-
let lastContext = null
|
|
40
|
-
|
|
41
|
-
const refresh = async () => {
|
|
42
|
-
if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext) return
|
|
43
|
-
lastStateTree = props.statefulLayout.stateTree
|
|
44
|
-
lastContext = props.statefulLayout.options.context ?? null
|
|
45
|
-
loading.value = true
|
|
46
|
-
items.value = await props.statefulLayout.getItems(props.modelValue)
|
|
47
|
-
loading.value = false
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!props.modelValue.layout.items) {
|
|
51
|
-
refresh()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
34
|
const fieldSlots = computed(() => {
|
|
55
35
|
const slots = getCompSlots(props.modelValue, props.statefulLayout)
|
|
56
36
|
|
|
@@ -58,12 +38,12 @@ export default defineComponent({
|
|
|
58
38
|
slots.default = () => {
|
|
59
39
|
/** @type {import('vue').VNode[]} */
|
|
60
40
|
const children = [h(VLabel, { text: fieldProps.value.label })]
|
|
61
|
-
if (loading.value) {
|
|
41
|
+
if (getItems.loading.value) {
|
|
62
42
|
children.push(h(VSkeletonLoader, { type: 'chip' }))
|
|
63
43
|
} else {
|
|
64
44
|
/** @type {import('vue').VNode[]} */
|
|
65
45
|
const checkboxes = []
|
|
66
|
-
for (const item of items.value) {
|
|
46
|
+
for (const item of getItems.items.value) {
|
|
67
47
|
let modelValue = false
|
|
68
48
|
if (props.modelValue.layout.multiple) {
|
|
69
49
|
modelValue = props.modelValue.data?.includes(item.value)
|
|
@@ -24,6 +24,10 @@ const props = defineProps({
|
|
|
24
24
|
const fieldProps = computed(() => {
|
|
25
25
|
const fieldProps = getInputProps(props.modelValue, props.statefulLayout, [], false)
|
|
26
26
|
fieldProps.readonly = true
|
|
27
|
+
fieldProps.clearable = fieldProps.clearable ?? !props.modelValue.skeleton.required
|
|
28
|
+
fieldProps['onClick:clear'] = () => {
|
|
29
|
+
props.statefulLayout.input(props.modelValue, null)
|
|
30
|
+
}
|
|
27
31
|
return fieldProps
|
|
28
32
|
})
|
|
29
33
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { VAutocomplete } from 'vuetify/components'
|
|
3
|
-
import { defineComponent, computed,
|
|
3
|
+
import { defineComponent, computed, h } from 'vue'
|
|
4
4
|
import { getInputProps, getCompSlots } from '../../utils/index.js'
|
|
5
|
+
import useGetItems from '../../composables/use-get-items.js'
|
|
5
6
|
import SelectItem from '../fragments/select-item.vue'
|
|
6
7
|
import SelectSelection from '../fragments/select-selection.vue'
|
|
7
8
|
|
|
@@ -13,54 +14,27 @@ export default defineComponent({
|
|
|
13
14
|
required: true
|
|
14
15
|
},
|
|
15
16
|
statefulLayout: {
|
|
16
|
-
|
|
17
|
+
/** @type import('vue').PropType<import('../../types.js').VjsfStatefulLayout> */
|
|
17
18
|
type: Object,
|
|
18
19
|
required: true
|
|
19
20
|
}
|
|
20
21
|
},
|
|
21
22
|
setup (props) {
|
|
22
|
-
|
|
23
|
-
const items = shallowRef([])
|
|
24
|
-
/** @type import('vue').Ref<boolean> */
|
|
25
|
-
const loading = ref(false)
|
|
26
|
-
/** @type import('vue').Ref<string> */
|
|
27
|
-
const search = ref('')
|
|
23
|
+
const getItems = useGetItems(props)
|
|
28
24
|
|
|
29
25
|
const fieldProps = computed(() => {
|
|
30
26
|
const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['multiple'])
|
|
31
27
|
if (props.modelValue.options.readOnly) fieldProps.menuProps = { modelValue: false }
|
|
32
28
|
fieldProps.noFilter = true
|
|
33
29
|
fieldProps['onUpdate:search'] = (/** @type string */searchValue) => {
|
|
34
|
-
search.value = searchValue
|
|
35
|
-
refresh()
|
|
30
|
+
getItems.search.value = searchValue
|
|
36
31
|
}
|
|
37
|
-
fieldProps
|
|
38
|
-
fieldProps.
|
|
39
|
-
fieldProps.
|
|
32
|
+
fieldProps.items = getItems.items.value
|
|
33
|
+
fieldProps.loading = getItems.loading.value
|
|
34
|
+
fieldProps.clearable = fieldProps.clearable ?? !props.modelValue.skeleton.required
|
|
40
35
|
return fieldProps
|
|
41
36
|
})
|
|
42
37
|
|
|
43
|
-
/** @type import('@json-layout/core').StateTree | null */
|
|
44
|
-
let lastStateTree = null
|
|
45
|
-
/** @type Record<string, any> | null */
|
|
46
|
-
let lastContext = null
|
|
47
|
-
/** @type string */
|
|
48
|
-
let lastSearch = ''
|
|
49
|
-
|
|
50
|
-
const refresh = async () => {
|
|
51
|
-
if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext && search.value === lastSearch) return
|
|
52
|
-
loading.value = true
|
|
53
|
-
items.value = await props.statefulLayout.getItems(props.modelValue, search.value)
|
|
54
|
-
lastStateTree = props.statefulLayout.stateTree
|
|
55
|
-
lastContext = props.statefulLayout.options.context ?? null
|
|
56
|
-
lastSearch = search.value
|
|
57
|
-
loading.value = false
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!props.modelValue.layout.items) {
|
|
61
|
-
refresh()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
38
|
const fieldSlots = computed(() => {
|
|
65
39
|
const slots = getCompSlots(props.modelValue, props.statefulLayout)
|
|
66
40
|
if (!slots.item) {
|
|
@@ -74,7 +48,7 @@ export default defineComponent({
|
|
|
74
48
|
slots.selection = (/** @type {any} */ context) => h(SelectSelection, {
|
|
75
49
|
multiple: props.modelValue.layout.multiple,
|
|
76
50
|
last: props.modelValue.layout.multiple && context.index === props.modelValue.data.length - 1,
|
|
77
|
-
item: context.item.raw
|
|
51
|
+
item: getItems.prepareSelectedItem(context.item.raw, context.item.value)
|
|
78
52
|
})
|
|
79
53
|
}
|
|
80
54
|
return slots
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { VRadioGroup, VRadio, VSkeletonLoader } from 'vuetify/components'
|
|
3
|
-
import { defineComponent, h, computed
|
|
3
|
+
import { defineComponent, h, computed } from 'vue'
|
|
4
4
|
import { getInputProps, getCompSlots } from '../../utils/index.js'
|
|
5
|
+
import useGetItems from '../../composables/use-get-items.js'
|
|
5
6
|
|
|
6
7
|
export default defineComponent({
|
|
7
8
|
props: {
|
|
@@ -17,42 +18,21 @@ export default defineComponent({
|
|
|
17
18
|
}
|
|
18
19
|
},
|
|
19
20
|
setup (props) {
|
|
20
|
-
|
|
21
|
-
const items = shallowRef([])
|
|
22
|
-
/** @type import('vue').Ref<boolean> */
|
|
23
|
-
const loading = ref(false)
|
|
21
|
+
const getItems = useGetItems(props)
|
|
24
22
|
|
|
25
23
|
const fieldProps = computed(() => {
|
|
26
24
|
const fieldProps = getInputProps(props.modelValue, props.statefulLayout)
|
|
27
25
|
return fieldProps
|
|
28
26
|
})
|
|
29
27
|
|
|
30
|
-
/** @type import('@json-layout/core').StateTree | null */
|
|
31
|
-
let lastStateTree = null
|
|
32
|
-
/** @type Record<string, any> | null */
|
|
33
|
-
let lastContext = null
|
|
34
|
-
|
|
35
|
-
const refresh = async () => {
|
|
36
|
-
if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext) return
|
|
37
|
-
lastStateTree = props.statefulLayout.stateTree
|
|
38
|
-
lastContext = props.statefulLayout.options.context ?? null
|
|
39
|
-
loading.value = true
|
|
40
|
-
items.value = await props.statefulLayout.getItems(props.modelValue)
|
|
41
|
-
loading.value = false
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!props.modelValue.layout.items) {
|
|
45
|
-
refresh()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
28
|
const fieldSlots = computed(() => {
|
|
49
29
|
const slots = getCompSlots(props.modelValue, props.statefulLayout)
|
|
50
30
|
/** @type {import('vue').VNode[]} */
|
|
51
31
|
const children = []
|
|
52
|
-
if (loading.value) {
|
|
32
|
+
if (getItems.loading.value) {
|
|
53
33
|
children.push(h(VSkeletonLoader, { type: 'chip' }))
|
|
54
34
|
} else {
|
|
55
|
-
for (const item of items.value) {
|
|
35
|
+
for (const item of getItems.items.value) {
|
|
56
36
|
children.push(h(VRadio, { label: item.title, value: item.value }))
|
|
57
37
|
}
|
|
58
38
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { VSelect } from 'vuetify/components'
|
|
3
|
-
import { defineComponent, h, computed
|
|
3
|
+
import { defineComponent, h, computed } from 'vue'
|
|
4
4
|
import { getInputProps, getCompSlots } from '../../utils/index.js'
|
|
5
|
+
import useGetItems from '../../composables/use-get-items.js'
|
|
5
6
|
import SelectItem from '../fragments/select-item.vue'
|
|
6
7
|
import SelectSelection from '../fragments/select-selection.vue'
|
|
7
8
|
|
|
@@ -19,38 +20,17 @@ export default defineComponent({
|
|
|
19
20
|
}
|
|
20
21
|
},
|
|
21
22
|
setup (props) {
|
|
22
|
-
|
|
23
|
-
const items = shallowRef([])
|
|
24
|
-
/** @type import('vue').Ref<boolean> */
|
|
25
|
-
const loading = ref(false)
|
|
23
|
+
const getItems = useGetItems(props)
|
|
26
24
|
|
|
27
25
|
const fieldProps = computed(() => {
|
|
28
26
|
const fieldProps = getInputProps(props.modelValue, props.statefulLayout, ['multiple'])
|
|
29
27
|
if (props.modelValue.options.readOnly) fieldProps.menuProps = { modelValue: false }
|
|
30
|
-
fieldProps.loading = loading.value
|
|
31
|
-
fieldProps.items = items.value
|
|
32
|
-
fieldProps
|
|
28
|
+
fieldProps.loading = getItems.loading.value
|
|
29
|
+
fieldProps.items = getItems.items.value
|
|
30
|
+
fieldProps.clearable = fieldProps.clearable ?? !props.modelValue.skeleton.required
|
|
33
31
|
return fieldProps
|
|
34
32
|
})
|
|
35
33
|
|
|
36
|
-
/** @type import('@json-layout/core').StateTree | null */
|
|
37
|
-
let lastStateTree = null
|
|
38
|
-
/** @type Record<string, any> | null */
|
|
39
|
-
let lastContext = null
|
|
40
|
-
|
|
41
|
-
const refresh = async () => {
|
|
42
|
-
if (props.statefulLayout.stateTree === lastStateTree && props.statefulLayout.options.context === lastContext) return
|
|
43
|
-
lastStateTree = props.statefulLayout.stateTree
|
|
44
|
-
lastContext = props.statefulLayout.options.context ?? null
|
|
45
|
-
loading.value = true
|
|
46
|
-
items.value = await props.statefulLayout.getItems(props.modelValue)
|
|
47
|
-
loading.value = false
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!props.modelValue.layout.items) {
|
|
51
|
-
refresh()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
34
|
const fieldSlots = computed(() => {
|
|
55
35
|
const slots = getCompSlots(props.modelValue, props.statefulLayout)
|
|
56
36
|
if (!slots.item) {
|
|
@@ -64,7 +44,7 @@ export default defineComponent({
|
|
|
64
44
|
slots.selection = (/** @type {any} */ context) => h(SelectSelection, {
|
|
65
45
|
multiple: props.modelValue.layout.multiple,
|
|
66
46
|
last: props.modelValue.layout.multiple && context.index === props.modelValue.data.length - 1,
|
|
67
|
-
item: context.item.raw
|
|
47
|
+
item: getItems.prepareSelectedItem(context.item.raw, context.item.value)
|
|
68
48
|
})
|
|
69
49
|
}
|
|
70
50
|
return slots
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { watch, shallowRef, ref, computed } from 'vue'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {{modelValue: import('@json-layout/core').StateNode, statefulLayout: import('../types.js').VjsfStatefulLayout}} props
|
|
5
|
+
*/
|
|
6
|
+
export default function (props) {
|
|
7
|
+
/** @type import('vue').Ref<import('@json-layout/vocabulary').SelectItems> */
|
|
8
|
+
const items = shallowRef([])
|
|
9
|
+
/** @type import('vue').Ref<boolean> */
|
|
10
|
+
const loading = ref(false)
|
|
11
|
+
/** @type import('vue').Ref<string> */
|
|
12
|
+
const search = ref('')
|
|
13
|
+
|
|
14
|
+
const fetchItems = async () => {
|
|
15
|
+
loading.value = true
|
|
16
|
+
items.value = await props.statefulLayout.getItems(props.modelValue, search.value)
|
|
17
|
+
loading.value = false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
watch(() => props.modelValue.itemsCacheKey, (newValue, oldValue) => {
|
|
21
|
+
if (newValue === oldValue) return
|
|
22
|
+
fetchItems()
|
|
23
|
+
}, { immediate: true })
|
|
24
|
+
|
|
25
|
+
watch(search, () => {
|
|
26
|
+
fetchItems()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {any} selectedItem
|
|
31
|
+
* @param {any} itemValue
|
|
32
|
+
*/
|
|
33
|
+
const prepareSelectedItem = (selectedItem, itemValue) => {
|
|
34
|
+
let item = selectedItem
|
|
35
|
+
// item and value are the same when the selection is not found in items list
|
|
36
|
+
if (selectedItem === itemValue) {
|
|
37
|
+
try {
|
|
38
|
+
item = props.statefulLayout.prepareSelectItem(props.modelValue, selectedItem)
|
|
39
|
+
if (item.value === undefined) item.value = itemValue
|
|
40
|
+
} catch (e) {
|
|
41
|
+
item = { value: itemValue }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return item
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { items, loading, search, prepareSelectedItem }
|
|
48
|
+
}
|
package/types/compat/v2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../src/compat/v2.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../src/compat/v2.js"],"names":[],"mappings":"AAoLA;;;;;;GAMG;AACH,kCALW,MAAM,+CAEN,MAAM,0BAkBhB;sBA1MqB,KAAK"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
declare const _default: import("vue").DefineComponent<{}, {
|
|
2
2
|
multiple: boolean;
|
|
3
|
-
item: import("../../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem;
|
|
4
3
|
itemProps: Record<string, any>;
|
|
4
|
+
item: import("../../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem;
|
|
5
5
|
$props: {
|
|
6
6
|
readonly multiple?: boolean | undefined;
|
|
7
|
-
readonly item?: import("../../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem | undefined;
|
|
8
7
|
readonly itemProps?: Record<string, any> | undefined;
|
|
8
|
+
readonly item?: import("../../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItem | undefined;
|
|
9
9
|
};
|
|
10
10
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
|
11
11
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection-group.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fragments/selection-group.vue.js"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"selection-group.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fragments/selection-group.vue.js"],"names":[],"mappings":";;QAUM,mFAAmF;cAAzE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,qBAAqB,CAAC;;;;QAKhF,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC;;;;;;;;;;;QAL7E,mFAAmF;cAAzE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,qBAAqB,CAAC;;;;QAKhF,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-field-menu.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fragments/text-field-menu.vue.js"],"names":[],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"text-field-menu.vue.d.ts","sourceRoot":"","sources":["../../../src/components/fragments/text-field-menu.vue.js"],"names":[],"mappings":";;;;;;;;;;6BAsIsC,GAAG;;;QACX,GAAG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/autocomplete.vue.js"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"autocomplete.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/autocomplete.vue.js"],"names":[],"mappings":";;QAYI,4EAA4E;cAAlE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,cAAc,CAAC;;;;QAKvE,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC;;;;;;;QAL/E,4EAA4E;cAAlE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,cAAc,CAAC;;;;QAKvE,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-group.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/radio-group.vue.js"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"radio-group.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/radio-group.vue.js"],"names":[],"mappings":";;QAUM,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC;;;;QAK7E,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC;;;;;;;QAL7E,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC;;;;QAK7E,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/select.vue.js"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"select.vue.d.ts","sourceRoot":"","sources":["../../../src/components/nodes/select.vue.js"],"names":[],"mappings":";;QAYI,4EAA4E;cAAlE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,cAAc,CAAC;;;;QAKzE,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC;;;;;;;QAL7E,4EAA4E;cAAlE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,cAAc,CAAC;;;;QAKzE,gFAAgF;cAAtE,OAAO,KAAK,EAAE,QAAQ,CAAC,OAAO,gBAAgB,EAAE,kBAAkB,CAAC"}
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {{modelValue: import('@json-layout/core').StateNode, statefulLayout: import('../types.js').VjsfStatefulLayout}} props
|
|
3
|
+
*/
|
|
4
|
+
export default function _default(props: {
|
|
5
|
+
modelValue: import('@json-layout/core').StateNode;
|
|
6
|
+
statefulLayout: import('../types.js').VjsfStatefulLayout;
|
|
7
|
+
}): {
|
|
8
|
+
items: import("vue").Ref<import("../../../node_modules/@json-layout/vocabulary/types/normalized-layout/types.js").SelectItems>;
|
|
9
|
+
loading: import("vue").Ref<boolean>;
|
|
10
|
+
search: import("vue").Ref<string>;
|
|
11
|
+
prepareSelectedItem: (selectedItem: any, itemValue: any) => any;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=use-get-items.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-get-items.d.ts","sourceRoot":"","sources":["../../src/composables/use-get-items.js"],"names":[],"mappings":"AAEA;;GAEG;AACH,wCAFW;IAAC,UAAU,EAAE,OAAO,mBAAmB,EAAE,SAAS,CAAC;IAAC,cAAc,EAAE,OAAO,aAAa,EAAE,kBAAkB,CAAA;CAAC;;;;wCA0B3G,GAAG,aACH,GAAG;EAiBf"}
|