@live-change/frontend-auto-form 0.2.7 → 0.2.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/ArrayInput.vue ADDED
@@ -0,0 +1,113 @@
1
+ <template>
2
+ <div>
3
+ <div class="mb-3" v-for="(value, index) in modelValue">
4
+ <div class="p-buttonset">
5
+ <Button class="" icon="pi pi-plus" @click="insertItem(index)"
6
+ label="Insert" />
7
+ <Button class="p-button-secondary" icon="pi pi-sync" @click="swapItem(index)"
8
+ label="Swap" />
9
+ <Button class="p-button-danger" icon="pi pi-trash" @click="removeItem(index)"
10
+ :label="`Remove #${ index+1 }`" />
11
+ </div>
12
+ <auto-input :modelValue="value" :definition="definition.of"
13
+ @update:modelValue="value => updateItem(index, value)"
14
+ :rootValue="props.rootValue" :propName="props.propName + '.' + index"
15
+ :i18n="i18n" />
16
+ </div>
17
+ <div>
18
+ <Button label="Add item" icon="pi pi-plus" @click="insertItem" />
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script setup>
24
+ import Button from "primevue/button";
25
+ import AutoInput from "./AutoInput.vue"
26
+
27
+ import { inputs, types } from './config.js'
28
+ import { computed, getCurrentInstance } from 'vue'
29
+ import { toRefs } from '@vueuse/core'
30
+
31
+ const props = defineProps({
32
+ modelValue: {
33
+ },
34
+ definition: {
35
+ type: Object
36
+ },
37
+ properties: {
38
+ type: Object,
39
+ default: () => ({})
40
+ },
41
+ rootValue: {
42
+ type: Object,
43
+ default: () => ({})
44
+ },
45
+ propName: {
46
+ type: String,
47
+ default: ''
48
+ },
49
+ i18n: {
50
+ type: String,
51
+ default: ''
52
+ }
53
+ })
54
+
55
+ const emit = defineEmits(['update:modelValue'])
56
+
57
+ const { value, definition, modelValue } = toRefs(props)
58
+
59
+ import { defaultData } from "@live-change/vue3-components"
60
+
61
+ import { useToast } from 'primevue/usetoast'
62
+ const toast = useToast()
63
+ import { useConfirm } from 'primevue/useconfirm'
64
+ const confirm = useConfirm()
65
+
66
+ function insertItem(index) {
67
+ const data = modelValue.value || []
68
+ const item = defaultData(definition.value.of)
69
+ data.splice(index ?? data.length, 0, item) /// TODO: default value
70
+ emit('update:modelValue', data)
71
+ toast.add({ severity: 'info', summary: 'Item added', life: 1500 })
72
+ }
73
+ function updateItem(index, value) {
74
+ const data = modelValue.value || []
75
+ data[index] = value
76
+ emit('update:modelValue', data)
77
+ }
78
+ function removeItem(index) {
79
+ confirm.require({
80
+ target: event.currentTarget,
81
+ message: `Are you sure you want to remove this item?`,
82
+ icon: 'pi pi-info-circle',
83
+ acceptClass: 'p-button-danger',
84
+ accept: async () => {
85
+ const data = modelValue.value || []
86
+ data.splice(index, 1)
87
+ emit('update:modelValue', data)
88
+ toast.add({ severity: 'info', summary: 'Item removed', life: 1500 })
89
+ },
90
+ reject: () => {
91
+ toast.add({ severity:'error', summary: 'Rejected', detail: 'You have rejected', life: 3000 })
92
+ }
93
+ })
94
+ }
95
+ function swapItem(index) {
96
+ const data = modelValue.value || []
97
+ if(index === 0) {
98
+ const popped = data.pop()
99
+ data.push(data.shift())
100
+ data.unshift(popped)
101
+ } else {
102
+ const tmp = data[index]
103
+ data[index] = data[index-1]
104
+ data[index-1] = tmp
105
+ }
106
+ emit('update:modelValue', data)
107
+ }
108
+
109
+ </script>
110
+
111
+ <style scoped>
112
+
113
+ </style>
package/AutoEditor.vue ADDED
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <div v-if="definition" class="grid formgrid p-fluid mt-2 mb-2">
3
+ <auto-field v-for="property in propertiesList" :key="property"
4
+ :modelValue="modelValue?.[property]"
5
+ @update:modelValue="value => updateModelProperty(property, value)"
6
+ :definition="definition.properties[property]"
7
+ :label="property"
8
+ :rootValue="props.rootValue" :propName="(propName ? propName + '.' : '') + property"
9
+ :i18n="i18n"
10
+ class="col-12" />
11
+ </div>
12
+ </template>
13
+
14
+ <script setup>
15
+ import AutoField from "./AutoField.vue"
16
+
17
+ import { computed, inject, getCurrentInstance } from 'vue'
18
+ import { toRefs } from '@vueuse/core'
19
+
20
+ const props = defineProps({
21
+ modelValue: {},
22
+ definition: {
23
+ type: Object
24
+ },
25
+ rootValue: {
26
+ type: Object,
27
+ default: () => ({})
28
+ },
29
+ propName: {
30
+ type: String,
31
+ default: ''
32
+ },
33
+ i18n: {
34
+ type: String,
35
+ default: ''
36
+ }
37
+ })
38
+
39
+ const { modelValue, definition, propName } = toRefs(props)
40
+
41
+ const emit = defineEmits(['update:modelValue'])
42
+
43
+ const propertiesList = computed(() => Object.keys(props.definition.properties)
44
+ .filter(key => props.definition.properties[key]))
45
+
46
+ function updateModelProperty(property, value) {
47
+ const data = modelValue.value || {}
48
+ data[property] = value
49
+ console.log("UPDATE MODEL", data)
50
+ emit('update:modelValue', data)
51
+ }
52
+
53
+
54
+ </script>
55
+
56
+ <style scoped>
57
+
58
+ </style>
package/AutoField.vue ADDED
@@ -0,0 +1,147 @@
1
+ <template>
2
+ <component v-if="fieldComponent && visible" :is="fieldComponent" v-bind="attributes"
3
+ @update:modelValue="value => emit('update:modelValue', value)" :i18n="i18n" />
4
+ <div v-else-if="visible" class="field" :class="fieldClass" :style="fieldStyle">
5
+ <label :for="uid">{{ t( label ) }}</label>
6
+ <slot>
7
+ <auto-input :modelValue="modelValue" :definition="definition"
8
+ :class="props.inputClass" :style="props.inputStyle"
9
+ :attributes="props.inputAttributes"
10
+ :propName="props.propName"
11
+ :rootValue="props.rootValue"
12
+ @update:modelValue="value => emit('update:modelValue', value)"
13
+ :id="uid"
14
+ :i18n="i18n" />
15
+ </slot>
16
+ <small v-if="validationResult" class="p-error">{{ t( 'errors.' + validationResult ) }}</small>
17
+ </div>
18
+ </template>
19
+
20
+ <script setup>
21
+
22
+ import AutoInput from "./AutoInput.vue"
23
+
24
+ import { inputs, types } from './config.js'
25
+ import { computed, getCurrentInstance } from 'vue'
26
+ import { toRefs } from '@vueuse/core'
27
+
28
+ import { useI18n } from 'vue-i18n'
29
+ const { t } = useI18n()
30
+
31
+ const props = defineProps({
32
+ modelValue: {},
33
+ error: {
34
+ type: String
35
+ },
36
+ definition: {
37
+ type: Object
38
+ },
39
+ name: {
40
+ type: String
41
+ },
42
+ label: {
43
+ type: String
44
+ },
45
+ class: {},
46
+ style: {},
47
+ inputClass: {},
48
+ inputStyle: {},
49
+ attributes: {
50
+ type: Object,
51
+ default: () => ({})
52
+ },
53
+ inputAttributes: {
54
+ type: Object,
55
+ default: () => ({})
56
+ },
57
+ rootValue: {
58
+ type: Object,
59
+ default: () => ({})
60
+ },
61
+ propName: {
62
+ type: String,
63
+ default: ''
64
+ },
65
+ i18n: {
66
+ type: String,
67
+ default: ''
68
+ }
69
+ })
70
+
71
+ const uid = 'field_'+getCurrentInstance().uid.toFixed().padStart(6, '0')
72
+
73
+ const emit = defineEmits(['update:modelValue'])
74
+
75
+ const { error, definition, modelValue } = toRefs(props)
76
+
77
+ const definitionIf = computed(() => {
78
+ if(definition.value?.if) {
79
+ if(definition.value?.if.function) {
80
+ return eval(`(${definition.value.if.function})`)
81
+ }
82
+ }
83
+ return false
84
+ })
85
+
86
+ const visible = computed(() => {
87
+ if(!definition.value) return false
88
+ if(definitionIf.value) {
89
+ return definitionIf.value({
90
+ source: definition.value,
91
+ props: props.rootValue,
92
+ propName: props.propName
93
+ })
94
+ }
95
+ return true
96
+ })
97
+
98
+ import { validateData } from "@live-change/vue3-components"
99
+
100
+ const validationResult = computed(() => {
101
+ const validationResult = validateData(definition.value, modelValue.value, 'validation')
102
+ const softValidationResult = validateData(definition.value, modelValue.value, 'softValidation')
103
+ return validationResult || softValidationResult || error.value
104
+ })
105
+
106
+ const inputConfig = computed(() => {
107
+ if(definition.value.input) return inputs[definition.value.input]
108
+ if(definition.value.type) return types[definition.value.type]
109
+ return inputs.default
110
+ })
111
+
112
+ const label = computed(() => props.i18n + (props.label || definition.value.label || props.name))
113
+
114
+ const fieldClass = computed(() => [inputConfig.value?.fieldClass, definition.value?.fieldClass, props.class, {
115
+ 'p-invalid': !!error.value
116
+ }])
117
+ const fieldStyle = computed(() => [inputConfig.value?.fieldStyle, definition.value?.fieldStyle, props.style])
118
+
119
+ const configAttributes = computed(() => {
120
+ const attributes = inputConfig.value?.fieldAttributes
121
+ if(!attributes) return attributes
122
+ if(typeof attributes == 'function') return attributes(definition.value)
123
+ return attributes
124
+ })
125
+
126
+ const attributes = computed(() => ({
127
+ ...(configAttributes.value),
128
+ ...(props.attributes),
129
+ label: props.label,
130
+ modelValue: props.modelValue,
131
+ definition: props.definition,
132
+ class: fieldClass.value,
133
+ style: fieldStyle.value,
134
+ inputClass: [props.inputClass, { 'p-invalid': !!validationResult.value }],
135
+ inputStyle: props.inputStyle,
136
+ rootValue: props.rootValue,
137
+ propName: props.propName,
138
+ }))
139
+
140
+ const fieldComponent = computed(() => inputConfig.value?.fieldComponent)
141
+
142
+
143
+ </script>
144
+
145
+ <style scoped>
146
+
147
+ </style>
package/AutoInput.vue CHANGED
@@ -1,12 +1,16 @@
1
1
  <template>
2
- <component v-if="inputConfig" :is="inputConfig.component" v-bind="properties" @update:modelValue="updateValue"
3
- :class="props.class" :style="props.style" />
4
- <div v-else class="font-bold text-red-600">No input found for definition {{ JSON.stringify(definition) }}</div>
2
+ <component v-if="inputConfig && visible" :is="inputConfig.component" v-bind="attributes"
3
+ @update:modelValue="updateValue" :class="inputClass" :style="inputStyle" />
4
+ <div v-else-if="visible" class="font-bold text-red-600">
5
+ No input found for definition:
6
+ <pre style="white-space: pre-wrap; word-wrap: break-word;">{{ JSON.stringify(definition, null, " ") }}</pre>
7
+ </div>
5
8
  </template>
6
9
 
7
10
  <script setup>
8
11
  import { inputs, types } from './config.js'
9
12
  import { computed, inject } from 'vue'
13
+ import { toRefs } from '@vueuse/core'
10
14
 
11
15
  const props = defineProps({
12
16
  modelValue: {
@@ -14,42 +18,94 @@
14
18
  definition: {
15
19
  type: Object
16
20
  },
17
- name: {
18
- type: String
19
- },
20
- class: {
21
- type: String
22
- },
23
- style: {
24
- type: String
25
- },
21
+ class: {},
22
+ style: {},
26
23
  properties: {
27
24
  type: Object,
28
25
  default: () => ({})
26
+ },
27
+ rootValue: {
28
+ type: Object,
29
+ default: () => ({})
30
+ },
31
+ propName: {
32
+ type: String,
33
+ default: ''
34
+ },
35
+ i18n: {
36
+ type: String,
37
+ default: ''
29
38
  }
30
39
  })
31
40
 
32
41
  const emit = defineEmits(['update:modelValue'])
33
42
 
34
- const form = inject('form')
43
+ const { definition, modelValue, propName } = toRefs(props)
35
44
 
36
- const value = computed(() => props.name ? form.getFieldValue(props.name) : props.modelValue)
37
- const definition = computed(() => props.name ? form.getFieldDefinition(props.name) : props.definition)
38
45
  const inputConfig = computed(() => {
39
- console.log("definition", definition.value)
40
46
  if(definition.value.input) return inputs[definition.value.input]
41
47
  if(definition.value.type) return types[definition.value.type]
42
48
  return inputs.default
43
49
  })
44
50
 
45
- const properties = computed(() => ({
46
- ...(inputConfig.value.properties),
47
- ...(props.properties),
48
- modelValue: value.value
51
+ const definitionIf = computed(() => {
52
+ if(definition.value?.if) {
53
+ if(definition.value?.if.function) {
54
+ return eval(`(${definition.value.if.function})`)
55
+ }
56
+ }
57
+ return false
58
+ })
59
+
60
+ const visible = computed(() => {
61
+ if(!definition.value) return false
62
+ if(definitionIf.value) {
63
+ console.log("DIF", propName.value, definitionIf.value, 'IN', props.rootValue)
64
+ return definitionIf.value({
65
+ source: definition.value,
66
+ props: props.rootValue,
67
+ propName: props.propName
68
+ })
69
+ }
70
+ return true
71
+ })
72
+
73
+ import { useI18n } from 'vue-i18n'
74
+ const { t, d, n } = useI18n()
75
+
76
+ const configAttributes = computed(() => {
77
+ const attributes = inputConfig.value?.attributes
78
+ if(!attributes) return attributes
79
+ if(typeof attributes == 'function') {
80
+ const fieldName = props.propName.split('.').pop()
81
+ console.log("PROPS", JSON.stringify(props))
82
+ console.log("PROPNAME", propName.value)
83
+ return attributes({
84
+ definition: definition.value,
85
+ i18n: props.i18n + fieldName,
86
+ propName: props.propName,
87
+ fieldName,
88
+ t, d, n
89
+ })
90
+ }
91
+ return attributes
92
+ })
93
+
94
+ const attributes = computed(() => ({
95
+ ...(configAttributes.value),
96
+ ...(props.attributes),
97
+ ...(definition.value.inputAttributes),
98
+ modelValue: modelValue.value,
99
+ definition: definition.value,
100
+ rootValue: props.rootValue,
101
+ propName: props.propName,
102
+ i18n: props.i18n
49
103
  }))
50
104
 
105
+ const inputClass = computed(() => [inputConfig.value?.inputClass, definition.value?.inputClass, props.class])
106
+ const inputStyle = computed(() => [inputConfig.value?.inputStyle, definition.value?.inputStyle, props.style])
107
+
51
108
  function updateValue(value) {
52
- if(props.name) form.setFieldValue(props.name, value)
53
109
  emit('update:modelValue', value)
54
110
  }
55
111
 
package/GroupField.vue ADDED
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <div v-if="visible" class="pl-3 border-left-3 border-400 mb-3" :class="fieldClass" :style="fieldStyle">
3
+ <h3>{{ t( i18n + label + ':title' ) }}</h3>
4
+ <auto-input :modelValue="modelValue" :definition="definition" :name="props.name"
5
+ :class="props.inputClass" :style="props.inputStyle"
6
+ :properties="props.inputAttributes"
7
+ :rootValue="props.rootValue" :propName="props.propName"
8
+ @update:modelValue="value => emit('update:modelValue', value)"
9
+ :i18n="props.i18n + props.propName.split('.').pop() + '.'" />
10
+ </div>
11
+ </template>
12
+
13
+ <script setup>
14
+ import AutoInput from "./AutoInput.vue"
15
+ import {inputs, types} from "./config";
16
+ import { computed, inject } from 'vue'
17
+ import { toRefs } from '@vueuse/core'
18
+
19
+ import { useI18n } from 'vue-i18n'
20
+ const { t } = useI18n()
21
+
22
+ const props = defineProps({
23
+ modelValue: {},
24
+ error: {
25
+ type: String
26
+ },
27
+ definition: {
28
+ type: Object
29
+ },
30
+ name: {
31
+ type: String
32
+ },
33
+ label: {
34
+ type: String
35
+ },
36
+ class: {},
37
+ style: {},
38
+ inputClass: {},
39
+ inputStyle: {},
40
+ attributes: {
41
+ type: Object,
42
+ default: () => ({})
43
+ },
44
+ inputAttributes: {
45
+ type: Object,
46
+ default: () => ({})
47
+ },
48
+ rootValue: {
49
+ type: Object,
50
+ default: () => ({})
51
+ },
52
+ propName: {
53
+ type: String,
54
+ default: ''
55
+ },
56
+ i18n: {
57
+ type: String,
58
+ default: ''
59
+ }
60
+ })
61
+
62
+ const emit = defineEmits(['update:modelValue'])
63
+
64
+ const { error, definition, modelValue } = toRefs(props)
65
+
66
+ const definitionIf = computed(() => {
67
+ if(definition.value?.if) {
68
+ if(definition.value?.if.function) {
69
+ return eval(`(${definition.value.if.function})`)
70
+ }
71
+ }
72
+ return false
73
+ })
74
+
75
+ const visible = computed(() => {
76
+ if(!definition.value) return false
77
+ if(definitionIf.value) {
78
+ return definitionIf.value({
79
+ source: definition.value,
80
+ props: props.rootValue,
81
+ propName: props.propName
82
+ })
83
+ }
84
+ return true
85
+ })
86
+
87
+ const inputConfig = computed(() => {
88
+ if(definition.value?.input) return inputs[definition.value.input]
89
+ if(definition.value?.type) return types[definition.value.type]
90
+ return inputs.default
91
+ })
92
+
93
+ const label = computed(() => props.label || definition.value?.label || props.name)
94
+
95
+ const fieldClass = computed(() => [inputConfig.value?.fieldClass, definition.value?.fieldClass, props.class, {
96
+ 'p-invalid': !!error.value
97
+ }])
98
+ const fieldStyle = computed(() => [inputConfig.value?.fieldStyle, definition.value?.fieldStyle, props.style])
99
+
100
+ </script>
101
+
102
+ <style scoped>
103
+
104
+ </style>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <div>
3
+
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+
9
+
10
+ const props = defineProps({
11
+ modelValue: {
12
+ },
13
+ definition: {
14
+ type: Object
15
+ },
16
+ properties: {
17
+ type: Object,
18
+ default: () => ({})
19
+ }
20
+ })
21
+
22
+ const emit = defineEmits(['update:modelValue'])
23
+
24
+ const { value, definition, modelValue } = toRefs(props)
25
+
26
+ </script>
27
+
28
+ <style scoped>
29
+
30
+ </style>
package/config.js CHANGED
@@ -5,20 +5,46 @@ export const inputs = {
5
5
  export const types = {
6
6
  }
7
7
 
8
- export function input(src, params) {
8
+ export function input(src, config) {
9
9
  return {
10
- component: defineAsyncComponent(src),
11
- params,
12
- with(params) {
13
- return { component: this.component, params }
10
+ component: src && defineAsyncComponent(src),
11
+ ...config,
12
+ with(config) {
13
+ return { component: this.component, ...config }
14
14
  }
15
15
  }
16
16
  }
17
17
 
18
18
  types.String = inputs.decimal = input( () => import('primevue/inputtext'))
19
+ inputs.textarea = input(() => import('primevue/textarea'), { attributes: { autoResize: true } })
20
+
19
21
  inputs.password = input(() => import('primevue/password'))
20
22
 
21
23
  const number = input(() => import('primevue/inputnumber'))
22
24
  inputs.integer = number
23
- types.Number = inputs.decimal = number.with({ mode: 'decimal' })
25
+ types.Number = inputs.decimal = number.with({ attributes: { mode: 'decimal' } })
26
+
27
+ types.Object = inputs.object = input(() => import('./AutoEditor.vue'), {
28
+ fieldComponent: defineAsyncComponent(() => import('./GroupField.vue'))
29
+ })
30
+
31
+ types.Array = inputs.list = input(() => import('./ArrayInput.vue'), {
32
+ fieldComponent: defineAsyncComponent(() => import('./GroupField.vue'))
33
+ })
34
+
35
+ types.Date = inputs.datetime = input(() => import('primevue/calendar'), { attributes: { showTime: true } })
36
+
37
+ inputs.select = input(() => import('primevue/dropdown'), {
38
+ attributes: (config) => {
39
+ const { definition, i18n, t } = config
40
+ console.log("SELECT", config)
41
+ return {
42
+ options: definition.options,
43
+ optionLabel: option => t(i18n + ':options.' + option)
44
+ }
45
+ }
46
+ })
24
47
 
48
+ inputs.duration = input(() => import('primevue/inputmask'), {
49
+ attributes: { mask: '99:99:99' }
50
+ })
package/index.js CHANGED
@@ -1,6 +1,12 @@
1
1
  import AutoInput from './AutoInput.vue'
2
+ import AutoField from './AutoField.vue'
3
+ import AutoEditor from './AutoEditor.vue'
2
4
 
3
- export { AutoInput }
5
+ export { AutoInput, AutoField, AutoEditor }
4
6
 
5
- import inputConfig from './config.js'
7
+ import * as inputConfig from './config.js'
6
8
  export { inputConfig }
9
+
10
+ import en from "./locales/en.json"
11
+ const locales = { en }
12
+ export { locales }
@@ -0,0 +1,5 @@
1
+ {
2
+ "errors": {
3
+ "empty": "This field is required."
4
+ }
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/frontend-auto-form",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "scripts": {
5
5
  "memDev": "lcli memDev --enableSessions --initScript ./init.js --dbAccess",
6
6
  "localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
@@ -20,7 +20,7 @@
20
20
  "debug": "node --inspect-brk server"
21
21
  },
22
22
  "dependencies": {
23
- "@fortawesome/fontawesome-free": "^6.1.1",
23
+ "@fortawesome/fontawesome-free": "^6.2.0",
24
24
  "@live-change/cli": "0.7.4",
25
25
  "@live-change/dao": "0.5.8",
26
26
  "@live-change/dao-vue3": "0.5.8",
@@ -28,8 +28,8 @@
28
28
  "@live-change/framework": "0.7.4",
29
29
  "@live-change/image-service": "0.3.2",
30
30
  "@live-change/session-service": "0.3.2",
31
- "@live-change/vue3-components": "0.2.15",
32
- "@live-change/vue3-ssr": "0.2.15",
31
+ "@live-change/vue3-components": "0.2.16",
32
+ "@live-change/vue3-ssr": "0.2.16",
33
33
  "@tiptap/extension-highlight": "^2.0.0-beta.33",
34
34
  "@tiptap/extension-underline": "2.0.0-beta.23",
35
35
  "@tiptap/starter-kit": "^2.0.0-beta.185",
@@ -66,5 +66,5 @@
66
66
  "author": "",
67
67
  "license": "ISC",
68
68
  "description": "",
69
- "gitHead": "ef577fb31dd857acb491bd7114e611638283fe6f"
69
+ "gitHead": "703c9723562ade7b2420e1b25b28ced51a96db09"
70
70
  }