@live-change/frontend-auto-form 0.9.61 → 0.9.63
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/front/locales/en.json +7 -0
- package/front/src/components/crud/AutoObjectIdentification.vue +46 -16
- package/front/src/components/crud/DefaultObjectIdentification.vue +85 -0
- package/front/src/components/crud/InjectedObjectIndentification.vue +1 -1
- package/front/src/components/crud/ModelEditor.vue +11 -10
- package/front/src/components/crud/ModelList.vue +43 -26
- package/front/src/components/crud/ModelSingle.vue +29 -11
- package/front/src/components/crud/ModelView.vue +22 -11
- package/front/src/components/crud/ObjectPath.vue +106 -0
- package/front/src/components/form/AutoField.vue +6 -3
- package/front/src/components/form/AutoInput.vue +2 -2
- package/front/src/components/form/GroupField.vue +2 -2
- package/front/src/components/form/ObjectInput.vue +72 -6
- package/front/src/components/form/ObjectPicker.vue +240 -2
- package/front/src/components/form/inputConfigInjection.js +5 -5
- package/front/src/components/form/provideAutoInputConfiguration.js +2 -0
- package/front/src/logic/editorData.js +25 -1
- package/front/src/logic/relations.js +71 -27
- package/index.js +4 -0
- package/package.json +13 -13
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
|
|
42
42
|
const { definition, modelValue, propName } = toRefs(props)
|
|
43
43
|
|
|
44
|
-
import {
|
|
45
|
-
const inputConfig = computed(() =>
|
|
44
|
+
import { injectInputConfigByDefinition } from './inputConfigInjection.js'
|
|
45
|
+
const inputConfig = computed(() => injectInputConfigByDefinition(definition.value))
|
|
46
46
|
|
|
47
47
|
const definitionIf = computed(() => {
|
|
48
48
|
if(definition.value?.if) {
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
return true
|
|
87
87
|
})
|
|
88
88
|
|
|
89
|
-
import {
|
|
90
|
-
const inputConfig = computed(() =>
|
|
89
|
+
import { injectInputConfigByDefinition } from './inputConfigInjection.js'
|
|
90
|
+
const inputConfig = computed(() => injectInputConfigByDefinition(definition.value))
|
|
91
91
|
|
|
92
92
|
import { validateData } from "@live-change/vue3-components"
|
|
93
93
|
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<div ref="selectElement" class="p-select p-component p-inputwrapper w-full" @click="toggleObjectPicker">
|
|
4
|
-
<span class="p-select-label p-placeholder" tabindex="0" role="combobox">
|
|
5
|
-
|
|
4
|
+
<span v-if="!model" class="p-select-label p-placeholder" tabindex="0" role="combobox">
|
|
5
|
+
<span v-if="isTypeSelectable">
|
|
6
|
+
Select object
|
|
7
|
+
</span>
|
|
8
|
+
<span v-else>
|
|
9
|
+
Select {{ objectModel }}
|
|
10
|
+
</span>
|
|
11
|
+
</span>
|
|
12
|
+
<span v-else class="p-select-label">
|
|
13
|
+
<ObjectIdentification
|
|
14
|
+
:objectType="objectType"
|
|
15
|
+
:object="model"
|
|
16
|
+
class=""
|
|
17
|
+
/>
|
|
6
18
|
</span>
|
|
7
19
|
<div class="p-select-dropdown" data-pc-section="dropdown">
|
|
8
20
|
<ChevronDownIcon />
|
|
9
21
|
</div>
|
|
10
22
|
</div>
|
|
11
23
|
|
|
24
|
+
<!-- <pre>objt = {{ objectType }}</pre> -->
|
|
25
|
+
|
|
12
26
|
<Popover ref="objectPickerPopover" :pt="{
|
|
13
27
|
root: {
|
|
14
28
|
class: 'object-picker-popover overflow-y-auto',
|
|
@@ -18,11 +32,13 @@
|
|
|
18
32
|
}
|
|
19
33
|
}
|
|
20
34
|
}">
|
|
21
|
-
<ObjectPicker
|
|
35
|
+
<ObjectPicker v-model="model" :definition="definition" :properties="properties"
|
|
36
|
+
:rootValue="rootValue" :propName="propName" :i18n="i18n" @selected="handleSelected" />
|
|
22
37
|
</Popover>
|
|
23
38
|
|
|
24
39
|
<!-- needed to autoload styles: -->
|
|
25
|
-
<Select class="hidden" />
|
|
40
|
+
<Select class="hidden" :options="[1,2,3]" />
|
|
41
|
+
|
|
26
42
|
</div>
|
|
27
43
|
</template>
|
|
28
44
|
|
|
@@ -31,8 +47,10 @@
|
|
|
31
47
|
import Select from 'primevue/select'
|
|
32
48
|
import Popover from 'primevue/popover'
|
|
33
49
|
import ObjectPicker from './ObjectPicker.vue'
|
|
50
|
+
import AutoObjectIdentification from '../crud/DefaultObjectIdentification.vue'
|
|
51
|
+
import { injectComponent } from "@live-change/vue3-components"
|
|
34
52
|
|
|
35
|
-
import { defineProps, defineEmits, toRefs, ref, defineModel } from 'vue'
|
|
53
|
+
import { defineProps, defineEmits, toRefs, ref, defineModel, computed } from 'vue'
|
|
36
54
|
import { useElementSize } from '@vueuse/core'
|
|
37
55
|
|
|
38
56
|
const props = defineProps({
|
|
@@ -42,14 +60,58 @@
|
|
|
42
60
|
properties: {
|
|
43
61
|
type: Object,
|
|
44
62
|
default: () => ({})
|
|
63
|
+
},
|
|
64
|
+
rootValue: {
|
|
65
|
+
type: Object,
|
|
66
|
+
default: () => ({})
|
|
67
|
+
},
|
|
68
|
+
propName: {
|
|
69
|
+
type: String,
|
|
70
|
+
default: ''
|
|
71
|
+
},
|
|
72
|
+
i18n: {
|
|
73
|
+
type: String,
|
|
74
|
+
default: ''
|
|
45
75
|
}
|
|
46
76
|
})
|
|
47
77
|
|
|
78
|
+
const { definition, properties, rootValue, propName, i18n: i18nPrefix } = toRefs(props)
|
|
79
|
+
|
|
48
80
|
const model = defineModel({
|
|
49
81
|
required: true
|
|
50
82
|
})
|
|
83
|
+
|
|
84
|
+
const isTypeSelectable = computed(() => {
|
|
85
|
+
return definition.value.type === 'any'
|
|
86
|
+
})
|
|
51
87
|
|
|
52
|
-
const
|
|
88
|
+
const objectType = computed(() => {
|
|
89
|
+
if(definition.value.type !== 'any') {
|
|
90
|
+
return definition.value.type
|
|
91
|
+
}
|
|
92
|
+
const typePropertyName = props.propName + 'Type'
|
|
93
|
+
const typePropertyPath = typePropertyName.split('.')
|
|
94
|
+
return typePropertyPath.reduce((acc, prop) => acc?.[prop], rootValue.value)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const objectModel = computed(() => {
|
|
98
|
+
const type = objectType.value
|
|
99
|
+
if(!type) return null
|
|
100
|
+
const [service, model] = type.split('_')
|
|
101
|
+
return model
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const ObjectIdentification = computed(() => {
|
|
105
|
+
const type = objectType.value
|
|
106
|
+
if(!type) return AutoObjectIdentification
|
|
107
|
+
const [service, model] = type.split('_')
|
|
108
|
+
return injectComponent({
|
|
109
|
+
name: 'ObjectIdentification',
|
|
110
|
+
type,
|
|
111
|
+
service: service,
|
|
112
|
+
model: model
|
|
113
|
+
}, AutoObjectIdentification)
|
|
114
|
+
})
|
|
53
115
|
|
|
54
116
|
const objectPickerPopover = ref()
|
|
55
117
|
const selectElement = ref()
|
|
@@ -59,6 +121,10 @@
|
|
|
59
121
|
objectPickerPopover.value.toggle(event)
|
|
60
122
|
}
|
|
61
123
|
|
|
124
|
+
const handleSelected = (object) => {
|
|
125
|
+
objectPickerPopover.value.hide()
|
|
126
|
+
}
|
|
127
|
+
|
|
62
128
|
</script>
|
|
63
129
|
|
|
64
130
|
<style>
|
|
@@ -1,10 +1,248 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="!model && !selectedType && typeOptions.length < 8">
|
|
4
|
+
<div class="p-2">
|
|
5
|
+
<div class="text-sm text-surface-600 dark:text-surface-400 mb-2">
|
|
6
|
+
{{ t('objectPicker.selectObjectType') }}
|
|
7
|
+
</div>
|
|
8
|
+
<div class="flex flex-col">
|
|
9
|
+
<div v-for="type in typeOptions" :key="type"
|
|
10
|
+
class="p-2 hover:bg-surface-100 dark:hover:bg-surface-800 cursor-pointer"
|
|
11
|
+
@click="selectedType = type">
|
|
12
|
+
{{ typeLabel(type) }}
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
4
16
|
</div>
|
|
17
|
+
<div v-else>
|
|
18
|
+
<div v-if="isTypeNeeded" class="bg-surface-100 dark:bg-surface-900 p-1 py-2 sticky top-0 z-10">
|
|
19
|
+
<div v-if="typeOptions.length > 1"
|
|
20
|
+
class="flex flex-col gap-2">
|
|
21
|
+
<!-- <label :for="`type-select-${uid}`">{{ t('objectPicker.selectObjectType') }}</label> -->
|
|
22
|
+
<Select v-model="selectedType" :options="typeOptions" :id="`type-select-${uid}`"
|
|
23
|
+
:placeholder="t('objectPicker.selectObjectType')"
|
|
24
|
+
:optionLabel="typeLabel" />
|
|
25
|
+
</div>
|
|
26
|
+
<div v-else>
|
|
27
|
+
<span>{{ t('objectPicker.currentType') }} {{ selectedTypeModel }}</span>
|
|
28
|
+
<span v-if="showServiceName">{{ t('objectPicker.fromService') }} {{ selectedTypeService }}</span>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div v-if="objectsPathRangeFunction">
|
|
33
|
+
<range-viewer :key="JSON.stringify(objectsPathRangeConfig)"
|
|
34
|
+
:pathFunction="objectsPathRangeFunction"
|
|
35
|
+
:canLoadTop="false" canDropBottom
|
|
36
|
+
loadBottomSensorSize="4000px" dropBottomSensorSize="3000px">
|
|
37
|
+
<template #empty>
|
|
38
|
+
<div class="text-xl text-surface-800 dark:text-surface-50 my-1 mx-4">
|
|
39
|
+
No <strong>
|
|
40
|
+
{{ pluralize(selectedTypeModel[0].toLowerCase() + selectedTypeModel.slice(1)) }}
|
|
41
|
+
</strong> found.
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
<template #default="{ item: object }">
|
|
45
|
+
<div @click="selectObject(object)"
|
|
46
|
+
class="flex flex-row items-center justify-between my-1 py-2 px-4
|
|
47
|
+
bg-surface-100 dark:bg-surface-900 hover:bg-surface-200 dark:hover:bg-surface-800">
|
|
48
|
+
<ObjectIdentification
|
|
49
|
+
:objectType="selectedTypeService + '_' + selectedTypeModel"
|
|
50
|
+
:object="object.to ?? object.id"
|
|
51
|
+
:data="object"
|
|
52
|
+
class=""
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
</range-viewer>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
<!-- <pre>isTypeNeeded = {{ isTypeNeeded }}</pre>
|
|
62
|
+
<pre>typePropertyName = {{ typePropertyName }}</pre>
|
|
63
|
+
<pre>typePropertyPath = {{ typePropertyPath }}</pre>
|
|
64
|
+
<pre>typeModel = {{ typeModel }}</pre> -->
|
|
65
|
+
<!-- <div>
|
|
66
|
+
<pre>objectsPathRangeConfig = {{ objectsPathRangeConfig }}</pre>
|
|
67
|
+
<pre>objectsPathRangeFunction = {{ objectsPathRangeFunction }}</pre>
|
|
68
|
+
<pre>selectedType = {{ selectedType }}</pre>
|
|
69
|
+
<pre>definition = {{ definition }}</pre>
|
|
70
|
+
<pre>properties = {{ properties }}</pre>
|
|
71
|
+
<pre>typeOptions = {{ typeOptions }}</pre>
|
|
72
|
+
<pre>model = {{ model }}</pre>
|
|
73
|
+
</div> -->
|
|
74
|
+
</div>
|
|
5
75
|
</template>
|
|
6
76
|
|
|
7
77
|
<script setup>
|
|
78
|
+
import Select from 'primevue/select'
|
|
79
|
+
import AutoObjectIdentification from '../crud/DefaultObjectIdentification.vue'
|
|
80
|
+
import { RangeViewer, injectComponent } from "@live-change/vue3-components"
|
|
81
|
+
|
|
82
|
+
import { defineProps, defineEmits, toRefs, ref, defineModel, computed, useId } from 'vue'
|
|
83
|
+
import pluralize from 'pluralize'
|
|
84
|
+
|
|
85
|
+
const uid = useId()
|
|
86
|
+
|
|
87
|
+
const props = defineProps({
|
|
88
|
+
definition: {
|
|
89
|
+
type: Object
|
|
90
|
+
},
|
|
91
|
+
properties: {
|
|
92
|
+
type: Object,
|
|
93
|
+
default: () => ({})
|
|
94
|
+
},
|
|
95
|
+
rootValue: {
|
|
96
|
+
type: Object,
|
|
97
|
+
default: () => ({})
|
|
98
|
+
},
|
|
99
|
+
propName: {
|
|
100
|
+
type: String,
|
|
101
|
+
default: ''
|
|
102
|
+
},
|
|
103
|
+
i18n: {
|
|
104
|
+
type: String,
|
|
105
|
+
default: ''
|
|
106
|
+
},
|
|
107
|
+
showServiceName: {
|
|
108
|
+
type: Boolean,
|
|
109
|
+
default: false
|
|
110
|
+
},
|
|
111
|
+
view: {
|
|
112
|
+
type: String,
|
|
113
|
+
default: 'range'
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const { definition, properties, rootValue, propName, i18n, view } = toRefs(props)
|
|
118
|
+
|
|
119
|
+
const model = defineModel({
|
|
120
|
+
required: true
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const emit = defineEmits(['selected'])
|
|
124
|
+
|
|
125
|
+
import { useI18n } from 'vue-i18n'
|
|
126
|
+
const { t: tI18n, te } = useI18n()
|
|
127
|
+
const t = (key, ...params) => tI18n(
|
|
128
|
+
te(i18n.value + propName.value + key)
|
|
129
|
+
? i18n.value + propName.value + key
|
|
130
|
+
: key, ...params
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
const isTypeNeeded = computed(() => {
|
|
134
|
+
return definition.value.type === 'any'
|
|
135
|
+
})
|
|
136
|
+
const typePropertyName = computed(() => props.propName + 'Type')
|
|
137
|
+
const typePropertyPath = computed(() => typePropertyName.value.split('.'))
|
|
138
|
+
const typeModel = computed({
|
|
139
|
+
get() {
|
|
140
|
+
return typePropertyPath.value.reduce((acc, prop) => acc?.[prop], rootValue.value)
|
|
141
|
+
},
|
|
142
|
+
set(value) {
|
|
143
|
+
const path = typePropertyPath.value
|
|
144
|
+
let data = rootValue.value
|
|
145
|
+
for(let i = 0; i < path.length - 1; i++) {
|
|
146
|
+
const prop = path[i]
|
|
147
|
+
if(data[prop] === undefined || data[prop] === null) {
|
|
148
|
+
data[prop] = {}
|
|
149
|
+
}
|
|
150
|
+
data = data[prop]
|
|
151
|
+
}
|
|
152
|
+
data[path.at(-1)] = value
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
import { getAllTypesWithCrud } from '../../logic/relations'
|
|
157
|
+
|
|
158
|
+
const typeOptions = computed(() => {
|
|
159
|
+
if(definition.value.type === 'any') {
|
|
160
|
+
return definition.value.types ?? getAllTypesWithCrud('read')
|
|
161
|
+
}
|
|
162
|
+
return [definition.value.type]
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const typeOptionsServices = computed(() => {
|
|
166
|
+
return Array.from(new Set(typeOptions.value.map(option => option.split('_')[0])))
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const selectedType = ref(null)
|
|
170
|
+
|
|
171
|
+
console.log("Type options", typeOptions.value)
|
|
172
|
+
if(typeModel.value) {
|
|
173
|
+
selectedType.value = typeModel.value
|
|
174
|
+
} else if(typeOptions.value.length === 1) {
|
|
175
|
+
selectedType.value = typeOptions.value[0]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const selectedTypeParsed = computed(() => {
|
|
179
|
+
if(selectedType.value) return selectedType.value.split('_')
|
|
180
|
+
return null
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const selectedTypeService = computed(() => {
|
|
184
|
+
if(selectedTypeParsed.value) return selectedTypeParsed.value[0]
|
|
185
|
+
return null
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
const selectedTypeModel = computed(() => {
|
|
189
|
+
if(selectedTypeParsed.value) return selectedTypeParsed.value[1]
|
|
190
|
+
return null
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const ObjectIdentification = computed(() => {
|
|
194
|
+
const [service, model] = selectedTypeParsed.value
|
|
195
|
+
return injectComponent({
|
|
196
|
+
name: 'ObjectIdentification',
|
|
197
|
+
type: service + '_' + model,
|
|
198
|
+
service: service,
|
|
199
|
+
model: model
|
|
200
|
+
}, AutoObjectIdentification)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
function typeLabel(option) {
|
|
204
|
+
const [service, model] = option.split('_')
|
|
205
|
+
if(typeOptionsServices.value.length === 1) {
|
|
206
|
+
return t('objectPicker.modelOption', { model })
|
|
207
|
+
}
|
|
208
|
+
return t('objectPicker.modelOptionWithService', { service, model })
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
import { useApi, usePath, live, reverseRange } from '@live-change/vue3-ssr'
|
|
212
|
+
const api = useApi()
|
|
213
|
+
const path = usePath()
|
|
214
|
+
|
|
215
|
+
const objectsPathRangeConfig = computed(() => {
|
|
216
|
+
if(!selectedTypeParsed.value) return null
|
|
217
|
+
const [service, model] = selectedTypeParsed.value
|
|
218
|
+
const serviceDefinition = api.metadata.api.value.services.find(s => s.name === service)
|
|
219
|
+
const modelDefinition = serviceDefinition.models[model]
|
|
220
|
+
return {
|
|
221
|
+
service,
|
|
222
|
+
model,
|
|
223
|
+
reverse: true,
|
|
224
|
+
view: modelDefinition?.crud?.[view.value ?? 'range']
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
const objectsPathRangeFunction = computed(() => {
|
|
228
|
+
const config = objectsPathRangeConfig.value
|
|
229
|
+
if(!config) return null
|
|
230
|
+
const rangeView = config.view
|
|
231
|
+
if(!path[config.service]) return null
|
|
232
|
+
if(!path[config.service][rangeView]) return null
|
|
233
|
+
return (range) => path[config.service][rangeView]({
|
|
234
|
+
//...ident,
|
|
235
|
+
...(config.reverse ? reverseRange(range) : range),
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
function selectObject(object) {
|
|
240
|
+
if(isTypeNeeded.value) {
|
|
241
|
+
typeModel.value = selectedType.value
|
|
242
|
+
}
|
|
243
|
+
model.value = object.to ?? object.id
|
|
244
|
+
emit('selected', object)
|
|
245
|
+
}
|
|
8
246
|
|
|
9
247
|
</script>
|
|
10
248
|
|
|
@@ -8,7 +8,7 @@ export function provideInputConfig(description, inputConfig) {
|
|
|
8
8
|
for(let key in description) {
|
|
9
9
|
for(let value of (description[key] instanceof Array ? description[key] : [description[key]])) {
|
|
10
10
|
const provideKey = `input:${key}=${value}`
|
|
11
|
-
console.log("PROVIDE
|
|
11
|
+
//console.log("PROVIDE INPUT CONFIG", provideKey)
|
|
12
12
|
provide(provideKey, {
|
|
13
13
|
inputConfig,
|
|
14
14
|
description
|
|
@@ -26,9 +26,9 @@ export function injectInputConfig(request, defaultInputConfig, factory) {
|
|
|
26
26
|
|
|
27
27
|
for(let key in request) {
|
|
28
28
|
const provideKey = `input:${key}=${request[key]}`
|
|
29
|
-
console.log("INJECT INPUT CONFIG PROVIDE KEY", provideKey)
|
|
29
|
+
//console.log("INJECT INPUT CONFIG PROVIDE KEY", provideKey)
|
|
30
30
|
const entry = inject(provideKey, null)
|
|
31
|
-
console.log("RESOLVED INPUT CONFIG", entry)
|
|
31
|
+
//console.log("RESOLVED INPUT CONFIG", entry)
|
|
32
32
|
if(!entry) continue
|
|
33
33
|
let isValid = true
|
|
34
34
|
for(let key in entry.description) {
|
|
@@ -37,7 +37,7 @@ export function injectInputConfig(request, defaultInputConfig, factory) {
|
|
|
37
37
|
if(!value.includes(entry.description[key])) isValid = false
|
|
38
38
|
} else if(value !== entry.description[key]) isValid = false
|
|
39
39
|
}
|
|
40
|
-
console.log("RESOLVED COMPONENT VALID", isValid, filter(entry))
|
|
40
|
+
//console.log("RESOLVED COMPONENT VALID", isValid, filter(entry))
|
|
41
41
|
if(isValid && filter(entry)) return entry.inputConfig
|
|
42
42
|
}
|
|
43
43
|
return factory ? defaultInputConfig() : defaultInputConfig
|
|
@@ -55,7 +55,7 @@ export function inputConfig(src, config) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
import deepmerge from 'deepmerge'
|
|
58
|
-
export function
|
|
58
|
+
export function injectInputConfigByDefinition(definition) {
|
|
59
59
|
let baseConfig
|
|
60
60
|
if(definition?.input && !baseConfig) baseConfig =
|
|
61
61
|
injectInputConfig({ name: definition.input }, null)
|
|
@@ -71,6 +71,8 @@ types.Boolean = inputs.switch = {
|
|
|
71
71
|
fieldComponent: defineAsyncComponent(() => import('./SwitchField.vue')),
|
|
72
72
|
}*/
|
|
73
73
|
|
|
74
|
+
types.any = inputs.object = inputConfig(() => import('./ObjectInput.vue'))
|
|
75
|
+
|
|
74
76
|
|
|
75
77
|
export function provideAutoInputConfiguration() {
|
|
76
78
|
for(let type in types) {
|
|
@@ -3,6 +3,27 @@ import { usePath, live, useApi } from '@live-change/vue3-ssr'
|
|
|
3
3
|
import { ref, computed, inject, watch } from 'vue'
|
|
4
4
|
import { synchronized, defaultData } from '@live-change/vue3-components'
|
|
5
5
|
|
|
6
|
+
function cyrb128(str) {
|
|
7
|
+
let h1 = 1779033703, h2 = 3144134277,
|
|
8
|
+
h3 = 1013904242, h4 = 2773480762;
|
|
9
|
+
for (let i = 0, k; i < str.length; i++) {
|
|
10
|
+
k = str.charCodeAt(i);
|
|
11
|
+
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
|
|
12
|
+
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
|
|
13
|
+
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
|
|
14
|
+
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
|
|
15
|
+
}
|
|
16
|
+
h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
|
|
17
|
+
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
|
|
18
|
+
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
|
|
19
|
+
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
|
|
20
|
+
h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1;
|
|
21
|
+
const data = new Uint32Array([h4>>>0, h3>>>0, h2>>>0, h1>>>0])
|
|
22
|
+
// convert to base64
|
|
23
|
+
const data8 = new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
24
|
+
return btoa(String.fromCharCode(...data8))
|
|
25
|
+
}
|
|
26
|
+
|
|
6
27
|
export default function editorData(options) {
|
|
7
28
|
if(!options) throw new Error('options must be provided')
|
|
8
29
|
|
|
@@ -66,8 +87,11 @@ export default function editorData(options) {
|
|
|
66
87
|
}
|
|
67
88
|
}
|
|
68
89
|
const isNew = (idKey ? (!identifiers[idKey]) : (!draftIdParts.every(key => identifiers[key])))
|
|
69
|
-
|
|
90
|
+
let draftId = (idKey ? identifiers[idKey]
|
|
70
91
|
: draftIdParts.map(key => JSON.stringify(identifiers[key])).join('_')) ?? 'new'
|
|
92
|
+
if(draftId.length > 16) {
|
|
93
|
+
draftId = cyrb128(draftId).slice(0, 16)
|
|
94
|
+
}
|
|
71
95
|
const draftIdentifiers = {
|
|
72
96
|
actionType: serviceName, action: crudMethods.read, targetType: modelName, target: draftId
|
|
73
97
|
}
|
|
@@ -15,6 +15,12 @@ function getWhats(relations) {
|
|
|
15
15
|
return relations.map(x => ensureArray(getWhat(x)))
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function getPropertyNames(relation) {
|
|
19
|
+
if(typeof relation === 'string') return relation[0].toLowerCase() + relation.slice(1)
|
|
20
|
+
if(relation.propertyNames) return relation.propertyNames
|
|
21
|
+
return ensureArray(relation.what).map(x => x[0].toLowerCase() + x.slice(1))
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
export const relationTypes = {
|
|
19
25
|
propertyOf: { singular: true, typed: true, owned: true },
|
|
20
26
|
boundTo: { singular: true, typed: true, owned: false },
|
|
@@ -58,10 +64,12 @@ export function getForwardRelations(model, api = useApi()) {
|
|
|
58
64
|
if(!Array.isArray(relations)) relations = [relations]
|
|
59
65
|
for(const relation of relations) {
|
|
60
66
|
const what = ensureArray(getWhat(relation))
|
|
67
|
+
const fields = ensureArray(getPropertyNames(relation))
|
|
61
68
|
const result = {
|
|
62
69
|
from: model,
|
|
63
70
|
relation: type,
|
|
64
|
-
what
|
|
71
|
+
what,
|
|
72
|
+
fields
|
|
65
73
|
}
|
|
66
74
|
results.push(result)
|
|
67
75
|
}
|
|
@@ -143,61 +151,83 @@ export function prepareObjectRelations(objectType, object, api = useApi()) {
|
|
|
143
151
|
})
|
|
144
152
|
|
|
145
153
|
if(anyRelationsTypes.includes(relation)) {
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
[
|
|
154
|
-
|
|
154
|
+
const views = []
|
|
155
|
+
const singular = relationTypes[relation].singular && fields.length < 2
|
|
156
|
+
|
|
157
|
+
if(singular) {
|
|
158
|
+
views.push({
|
|
159
|
+
name: 'read',
|
|
160
|
+
identifiers: {
|
|
161
|
+
[fields[0]+'Type']: objectType,
|
|
162
|
+
[fields[0]]: object
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
} else {
|
|
166
|
+
for(const field of fields) {
|
|
167
|
+
const possibleFieldTypes = (relationConfig[field+'Types'] ?? [])
|
|
168
|
+
.concat(relationConfig.parentsTypes ?? [])
|
|
169
|
+
if(possibleFieldTypes.length === 0 || possibleFieldTypes.includes(objectType)) {
|
|
170
|
+
const name = 'rangeBy' + field[0].toUpperCase() + field.slice(1)
|
|
171
|
+
if(from.crud?.[name]) views.push({
|
|
172
|
+
name,
|
|
173
|
+
identifiers: {
|
|
174
|
+
[field+'Type']: objectType,
|
|
175
|
+
[field]: object
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
}
|
|
155
179
|
}
|
|
156
180
|
}
|
|
181
|
+
/* console.error("relation", relation, 'from', from, "type", relationTypes[relation],
|
|
182
|
+
"what", what, "fields", fields) */
|
|
157
183
|
return {
|
|
158
184
|
model: from.name,
|
|
159
185
|
service: from.serviceName,
|
|
160
186
|
fields,
|
|
161
187
|
relation,
|
|
162
188
|
what,
|
|
163
|
-
|
|
189
|
+
views,
|
|
164
190
|
access,
|
|
165
|
-
singular
|
|
191
|
+
singular
|
|
166
192
|
}
|
|
167
193
|
} else {
|
|
194
|
+
const views = []
|
|
168
195
|
const singular = relationTypes[relation].singular && what.length < 2
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
? undefined
|
|
174
|
-
: typeView
|
|
175
|
-
) ?? undefined
|
|
176
|
-
const identifiers = []
|
|
196
|
+
const name = 'rangeBy_' + objectType
|
|
197
|
+
const typeView = from.crud?.[name]
|
|
198
|
+
? name
|
|
199
|
+
: undefined
|
|
177
200
|
if(typeView) {
|
|
178
|
-
|
|
179
|
-
|
|
201
|
+
views.push({
|
|
202
|
+
name: typeView,
|
|
203
|
+
identifiers: {
|
|
204
|
+
[model[0].toLowerCase() + model.slice(1)]: object
|
|
205
|
+
}
|
|
180
206
|
})
|
|
181
207
|
} else {
|
|
182
208
|
for(let i = 0; i < what.length; i++) {
|
|
183
209
|
if(what[i] !== objectType) continue
|
|
184
210
|
const propertyName = relationConfig.propertyNames?.[i]
|
|
185
211
|
?? model[0].toLowerCase() + model.slice(1)
|
|
186
|
-
|
|
187
|
-
|
|
212
|
+
const name = 'rangeBy' + propertyName[0].toUpperCase() + propertyName.slice(1)
|
|
213
|
+
if(!from.crud?.[name]) continue
|
|
214
|
+
views.push({
|
|
215
|
+
name,
|
|
216
|
+
identifiers: {
|
|
217
|
+
[propertyName]: object
|
|
218
|
+
}
|
|
188
219
|
})
|
|
189
220
|
}
|
|
190
221
|
}
|
|
191
|
-
console.log(objectType, "
|
|
222
|
+
console.log(objectType, "VIEWS", views, "FROM", from, "SINGULAR", singular)
|
|
192
223
|
return {
|
|
193
224
|
model: from.name,
|
|
194
225
|
service: from.serviceName,
|
|
195
226
|
fields,
|
|
196
227
|
relation,
|
|
197
228
|
what,
|
|
198
|
-
identifiers,
|
|
199
229
|
access,
|
|
200
|
-
|
|
230
|
+
views,
|
|
201
231
|
singular
|
|
202
232
|
}
|
|
203
233
|
}
|
|
@@ -205,3 +235,17 @@ export function prepareObjectRelations(objectType, object, api = useApi()) {
|
|
|
205
235
|
|
|
206
236
|
return preparedBackwardRelations
|
|
207
237
|
}
|
|
238
|
+
|
|
239
|
+
export function getAllPossibleTypes(api = useApi(), filter = () => true,) {
|
|
240
|
+
return Object.entries(api.services).map(
|
|
241
|
+
([serviceName, service]) => Object.values(service.models).filter(o => filter(o, service, serviceName)).map(
|
|
242
|
+
model => `${serviceName}_${model.name}`
|
|
243
|
+
).flat()
|
|
244
|
+
).flat()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function getAllTypesWithCrud(crud, api = useApi()) {
|
|
248
|
+
return getAllPossibleTypes(api, (model, service, serviceName) => {
|
|
249
|
+
if(model.crud?.[crud]) return true
|
|
250
|
+
})
|
|
251
|
+
}
|
package/index.js
CHANGED
|
@@ -34,6 +34,10 @@ import ModelList from './front/src/components/crud/ModelList.vue'
|
|
|
34
34
|
export { ModelList }
|
|
35
35
|
import EditorButtons from './front/src/components/crud/EditorButtons.vue'
|
|
36
36
|
export { EditorButtons }
|
|
37
|
+
import DefaultObjectIdentification from './front/src/components/crud/DefaultObjectIdentification.vue'
|
|
38
|
+
export { DefaultObjectIdentification }
|
|
39
|
+
import ObjectPath from './front/src/components/crud/ObjectPath.vue'
|
|
40
|
+
export { ObjectPath }
|
|
37
41
|
import AutoObjectIdentification from './front/src/components/crud/AutoObjectIdentification.vue'
|
|
38
42
|
export { AutoObjectIdentification }
|
|
39
43
|
|