@live-change/frontend-auto-form 0.8.50 → 0.8.51

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 CHANGED
@@ -1,13 +1,49 @@
1
1
  <template>
2
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 }`" />
3
+ <div class="mb-4 border-bottom-1" v-for="(value, index) in modelValue">
4
+ <div class="flex flex-row align-items-center justify-content-between">
5
+
6
+ <div class="text-2xl">
7
+ {{ te(i18n.slice(0,-1)+':itemTitle')
8
+ ? t(i18n.slice(0,-1)+':itemTitle', { ...value, index: index+1 })
9
+ : `#${index + 1}` }}
10
+ </div>
11
+
12
+ <div class="flex flex-row">
13
+ <Button class="mx-1" icon="pi pi-plus" @click="insertItem(index)"
14
+ style="transform: scale(0.9)"
15
+ rounded
16
+ severity="success"
17
+ size="small"
18
+ v-ripple
19
+ aria-label="Insert before" />
20
+ <Button v-if="index > 0"
21
+ style="transform: scale(0.9)"
22
+ class="mx-1"
23
+ icon="pi pi-arrow-up" @click="swapItem(index)"
24
+ severity="secondary"
25
+ rounded
26
+ size="small"
27
+ aria-label="Move up" />
28
+ <Button v-if="index < modelValue.length - 1"
29
+ style="transform: scale(0.9)"
30
+ class="mx-1"
31
+ icon="pi pi-arrow-down" @click="swapItem(index + 1)"
32
+ severity="secondary"
33
+ rounded
34
+ v-ripple
35
+ size="small"
36
+ aria-label="Move down" />
37
+ <Button class="mx-1"
38
+ style="transform: scale(0.9)"
39
+ icon="pi pi-trash" @click="removeItem(index)"
40
+ severity="danger"
41
+ rounded
42
+ size="small"
43
+ v-ripple
44
+ :aria-label="`Remove #${ index+1 }`" />
45
+ </div>
46
+
11
47
  </div>
12
48
  <auto-input :modelValue="value" :definition="definition.of"
13
49
  @update:modelValue="value => updateItem(index, value)"
@@ -15,14 +51,17 @@
15
51
  :i18n="i18n" />
16
52
  </div>
17
53
  <div>
18
- <Button class="w-10rem" :label="t('autoform.addItem')" icon="pi pi-plus" @click="ev => insertItem()" />
54
+ <Button severity="success"
55
+ :label="te(i18n.slice(0,-1)+':addItem') ? t(i18n.slice(0,-1)+':addItem') : t('autoform.addItem')"
56
+ icon="pi pi-plus"
57
+ @click="ev => insertItem()" />
19
58
  </div>
20
59
  </div>
21
60
  </template>
22
61
 
23
62
  <script setup>
24
63
  import { useI18n } from 'vue-i18n'
25
- const { t, n, d } = useI18n()
64
+ const { t, te, n, d } = useI18n()
26
65
 
27
66
  import Button from "primevue/button";
28
67
  import AutoInput from "./AutoInput.vue"
package/AutoField.vue CHANGED
@@ -13,7 +13,17 @@
13
13
  :id="uid"
14
14
  :i18n="i18n" />
15
15
  </slot>
16
- <small v-if="validationResult" class="p-error">{{ t( 'errors.' + validationResult ) }}</small>
16
+ <div>
17
+ <small v-if="validationResult" class="p-error mt-1">
18
+ {{ (typeof validationResult === 'object')
19
+ ? t( 'errors.' + validationResult.error, validationResult.validator )
20
+ : t( 'errors.' + validationResult ) }}
21
+ </small>
22
+ <small v-if="maxLengthValidation" style="float: right" class="mt-1"
23
+ :class="{ 'p-error': props.modelValue?.length > maxLengthValidation.length }">
24
+ {{ t( 'info.maxLength', { maxLength: maxLengthValidation.length, length: props.modelValue?.length ?? 0 }) }}
25
+ </small>
26
+ </div>
17
27
  </div>
18
28
  </template>
19
29
 
@@ -97,11 +107,24 @@
97
107
  import { validateData } from "@live-change/vue3-components"
98
108
  const appContext = getCurrentInstance().appContext
99
109
  const validationResult = computed(() => {
100
- const validationResult = validateData(definition.value, modelValue.value, 'validation', appContext)
101
- const softValidationResult = validateData(definition.value, modelValue.value, 'softValidation', appContext)
110
+ const validationResult = validateData(definition.value, modelValue.value, 'validation', appContext,
111
+ props.propName, props.rootValue, true)
112
+ const softValidationResult = validateData(definition.value, modelValue.value, 'softValidation', appContext,
113
+ props.propName, props.rootValue, true)
102
114
  return validationResult || softValidationResult || error.value
103
115
  })
104
116
 
117
+ function findValidation(name) {
118
+ if(definition.value.softValidation)
119
+ for(const validator of definition.value.softValidation)
120
+ if(validator.name === name) return validator
121
+ if(definition.value.validation)
122
+ for(const validator of definition.value.validation)
123
+ if(validator.name === name) return validator
124
+ }
125
+
126
+ const maxLengthValidation = computed(() => findValidation('maxLength')?.params)
127
+
105
128
  const config = inject('auto-form', {
106
129
  inputs: {},
107
130
  types: {}
package/AutoInput.vue CHANGED
@@ -11,6 +11,7 @@
11
11
  <script setup>
12
12
  import { inputs, types } from './config.js'
13
13
  import { computed, inject, toRefs } from 'vue'
14
+ import deepmerge from 'deepmerge';
14
15
 
15
16
  const props = defineProps({
16
17
  modelValue: {
@@ -47,12 +48,13 @@
47
48
  types: {}
48
49
  })
49
50
  const inputConfig = computed(() => {
50
- if(definition.value.input) return config.inputs?.[definition.value.input] ?? inputs[definition.value.input]
51
- if(definition.value.type) return config.types?.[definition.value.type] ?? types[definition.value.type]
52
- return {
53
- ...(config.inputs?.default ?? inputs.default),
54
- ...definition?.autoForm?.config, // possible to modify config per input
55
- }
51
+ let baseConfig
52
+ if(definition.value.input && !baseConfig) baseConfig =
53
+ config.inputs?.[definition.value.input] ?? inputs[definition.value.input]
54
+ if(definition.value.type && !baseConfig) baseConfig =
55
+ config.types?.[definition.value.type] ?? types[definition.value.type]
56
+ if(!baseConfig) baseConfig = config.inputs?.default ?? inputs.default
57
+ return deepmerge(baseConfig, definition.value?.inputConfig ?? {}) // possible to modify config per input
56
58
  })
57
59
 
58
60
  const definitionIf = computed(() => {
package/Calendar.vue ADDED
@@ -0,0 +1,263 @@
1
+ <template>
2
+ <PrimeVueCalendar v-bind="props" v-model="convertedDate" />
3
+ </template>
4
+
5
+ <script setup>
6
+ import PrimeVueCalendar from 'primevue/calendar'
7
+
8
+ import { defineProps, defineModel, computed } from 'vue'
9
+
10
+ const model = defineModel({
11
+ })
12
+
13
+ const convertedDate = computed({ // convert from iso to Date
14
+ get: () => model.value ? new Date(model.value) : null,
15
+ set: val => model.value = val ? new Date(val) : null
16
+ })
17
+
18
+ const props = defineProps({
19
+ selectionMode: {
20
+ type: String,
21
+ default: 'single'
22
+ },
23
+ dateFormat: {
24
+ type: String,
25
+ default: null
26
+ },
27
+ inline: {
28
+ type: Boolean,
29
+ default: false
30
+ },
31
+ showOtherMonths: {
32
+ type: Boolean,
33
+ default: true
34
+ },
35
+ selectOtherMonths: {
36
+ type: Boolean,
37
+ default: false
38
+ },
39
+ showIcon: {
40
+ type: Boolean,
41
+ default: false
42
+ },
43
+ iconDisplay: {
44
+ type: String,
45
+ default: 'button'
46
+ },
47
+ icon: {
48
+ type: String,
49
+ default: undefined
50
+ },
51
+ prevIcon: {
52
+ type: String,
53
+ default: undefined
54
+ },
55
+ nextIcon: {
56
+ type: String,
57
+ default: undefined
58
+ },
59
+ incrementIcon: {
60
+ type: String,
61
+ default: undefined
62
+ },
63
+ decrementIcon: {
64
+ type: String,
65
+ default: undefined
66
+ },
67
+ numberOfMonths: {
68
+ type: Number,
69
+ default: 1
70
+ },
71
+ responsiveOptions: Array,
72
+ breakpoint: {
73
+ type: String,
74
+ default: '769px'
75
+ },
76
+ view: {
77
+ type: String,
78
+ default: 'date'
79
+ },
80
+ minDate: {
81
+ type: Date,
82
+ value: null
83
+ },
84
+ maxDate: {
85
+ type: Date,
86
+ value: null
87
+ },
88
+ disabledDates: {
89
+ type: Array,
90
+ value: null
91
+ },
92
+ disabledDays: {
93
+ type: Array,
94
+ value: null
95
+ },
96
+ maxDateCount: {
97
+ type: Number,
98
+ value: null
99
+ },
100
+ showOnFocus: {
101
+ type: Boolean,
102
+ default: true
103
+ },
104
+ autoZIndex: {
105
+ type: Boolean,
106
+ default: true
107
+ },
108
+ baseZIndex: {
109
+ type: Number,
110
+ default: 0
111
+ },
112
+ showButtonBar: {
113
+ type: Boolean,
114
+ default: false
115
+ },
116
+ shortYearCutoff: {
117
+ type: String,
118
+ default: '+10'
119
+ },
120
+ showTime: {
121
+ type: Boolean,
122
+ default: false
123
+ },
124
+ timeOnly: {
125
+ type: Boolean,
126
+ default: false
127
+ },
128
+ hourFormat: {
129
+ type: String,
130
+ default: '24'
131
+ },
132
+ stepHour: {
133
+ type: Number,
134
+ default: 1
135
+ },
136
+ stepMinute: {
137
+ type: Number,
138
+ default: 1
139
+ },
140
+ stepSecond: {
141
+ type: Number,
142
+ default: 1
143
+ },
144
+ showSeconds: {
145
+ type: Boolean,
146
+ default: false
147
+ },
148
+ hideOnDateTimeSelect: {
149
+ type: Boolean,
150
+ default: false
151
+ },
152
+ hideOnRangeSelection: {
153
+ type: Boolean,
154
+ default: false
155
+ },
156
+ timeSeparator: {
157
+ type: String,
158
+ default: ':'
159
+ },
160
+ showWeek: {
161
+ type: Boolean,
162
+ default: false
163
+ },
164
+ manualInput: {
165
+ type: Boolean,
166
+ default: true
167
+ },
168
+ appendTo: {
169
+ type: [String, Object],
170
+ default: 'body'
171
+ },
172
+ variant: {
173
+ type: String,
174
+ default: null
175
+ },
176
+ invalid: {
177
+ type: Boolean,
178
+ default: false
179
+ },
180
+ disabled: {
181
+ type: Boolean,
182
+ default: false
183
+ },
184
+ readonly: {
185
+ type: Boolean,
186
+ default: false
187
+ },
188
+ placeholder: {
189
+ type: String,
190
+ default: null
191
+ },
192
+ name: {
193
+ type: String,
194
+ default: null
195
+ },
196
+ id: {
197
+ type: String,
198
+ default: null
199
+ },
200
+ inputId: {
201
+ type: String,
202
+ default: null
203
+ },
204
+ inputClass: {
205
+ type: [String, Object],
206
+ default: null
207
+ },
208
+ inputStyle: {
209
+ type: Object,
210
+ default: null
211
+ },
212
+ panelClass: {
213
+ type: [String, Object],
214
+ default: null
215
+ },
216
+ panelStyle: {
217
+ type: Object,
218
+ default: null
219
+ },
220
+ todayButtonProps: {
221
+ type: Object,
222
+ default() {
223
+ return { severity: 'secondary', text: true, size: 'small' };
224
+ }
225
+ },
226
+ clearButtonProps: {
227
+ type: Object,
228
+ default() {
229
+ return { severity: 'secondary', text: true, size: 'small' };
230
+ }
231
+ },
232
+ navigatorButtonProps: {
233
+ type: Object,
234
+ default() {
235
+ return { severity: 'secondary', text: true, rounded: true };
236
+ }
237
+ },
238
+ timepickerButtonProps: {
239
+ type: Object,
240
+ default() {
241
+ return { severity: 'secondary', text: true, rounded: true };
242
+ }
243
+ },
244
+ fluid: {
245
+ type: Boolean,
246
+ default: null
247
+ },
248
+ ariaLabelledby: {
249
+ type: String,
250
+ default: null
251
+ },
252
+ ariaLabel: {
253
+ type: String,
254
+ default: null
255
+ }
256
+ })
257
+
258
+
259
+ </script>
260
+
261
+ <style scoped>
262
+
263
+ </style>
package/GroupField.vue CHANGED
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div v-if="visible" class="pl-3 border-left-3 border-400 mb-3" :class="fieldClass" :style="fieldStyle">
2
+ <div v-if="visible" class="pl-5 mb-3" :class="fieldClass" :style="fieldStyle">
3
3
  <h3>{{ t( i18n + label + ':title' ) }}</h3>
4
4
  <auto-input :modelValue="modelValue" :definition="definition" :name="props.name"
5
5
  :class="props.inputClass" :style="props.inputStyle"
package/config.js CHANGED
@@ -33,7 +33,7 @@ types.Array = inputs.list = input(() => import('./ArrayInput.vue'), {
33
33
  fieldComponent: defineAsyncComponent(() => import('./GroupField.vue'))
34
34
  })
35
35
 
36
- types.Date = inputs.datetime = input(() => import('primevue/calendar'), { attributes: { showTime: true } })
36
+ types.Date = inputs.datetime = input(() => import('./Calendar.vue'), { attributes: { showTime: true } })
37
37
 
38
38
  inputs.select = input(() => import('primevue/dropdown'), {
39
39
  attributes: (config) => {
@@ -68,3 +68,7 @@ inputs.multiselect = input(() => import('primevue/multiselect'), {
68
68
  inputs.duration = input(() => import('primevue/inputmask'), {
69
69
  attributes: { mask: '99:99:99' }
70
70
  })
71
+
72
+ types.Image = inputs.image = input(
73
+ async () => (await import('@live-change/image-frontend')).ImageInput
74
+ )
package/locales/en.json CHANGED
@@ -1,5 +1,14 @@
1
1
  {
2
2
  "errors": {
3
- "empty": "This field is required."
3
+ "empty": "This field is required.",
4
+ "tooShort": "Too short, minimum {length} characters.",
5
+ "tooLong": "Too long, maximum {length} characters."
6
+ },
7
+ "info": {
8
+ "minLength": "Minimum length - {minLength} characters.",
9
+ "maxLength": "{length} / {maxLength}"
10
+ },
11
+ "autoform": {
12
+ "addItem": "Add next item"
4
13
  }
5
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/frontend-auto-form",
3
- "version": "0.8.50",
3
+ "version": "0.8.51",
4
4
  "scripts": {
5
5
  "memDev": "node server/start.js memDev --enableSessions --initScript ./init.js --dbAccess",
6
6
  "localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
@@ -22,15 +22,16 @@
22
22
  "type": "module",
23
23
  "dependencies": {
24
24
  "@fortawesome/fontawesome-free": "^6.5.2",
25
- "@live-change/cli": "^0.8.50",
26
- "@live-change/dao": "^0.8.50",
27
- "@live-change/dao-vue3": "^0.8.50",
28
- "@live-change/dao-websocket": "^0.8.50",
29
- "@live-change/framework": "^0.8.50",
30
- "@live-change/image-service": "^0.8.50",
31
- "@live-change/session-service": "^0.8.50",
32
- "@live-change/vue3-components": "^0.8.50",
33
- "@live-change/vue3-ssr": "^0.8.50",
25
+ "@live-change/cli": "^0.8.51",
26
+ "@live-change/dao": "^0.8.51",
27
+ "@live-change/dao-vue3": "^0.8.51",
28
+ "@live-change/dao-websocket": "^0.8.51",
29
+ "@live-change/framework": "^0.8.51",
30
+ "@live-change/image-frontend": "^0.8.51",
31
+ "@live-change/image-service": "^0.8.51",
32
+ "@live-change/session-service": "^0.8.51",
33
+ "@live-change/vue3-components": "^0.8.51",
34
+ "@live-change/vue3-ssr": "^0.8.51",
34
35
  "@vueuse/core": "^10.11.0",
35
36
  "codeceptjs-assert": "^0.0.5",
36
37
  "compression": "^1.7.4",
@@ -51,7 +52,7 @@
51
52
  "vue3-scroll-border": "0.1.6"
52
53
  },
53
54
  "devDependencies": {
54
- "@live-change/codeceptjs-helper": "^0.8.50",
55
+ "@live-change/codeceptjs-helper": "^0.8.51",
55
56
  "codeceptjs": "^3.5.12",
56
57
  "generate-password": "1.7.1",
57
58
  "playwright": "^1.41.2",
@@ -62,5 +63,5 @@
62
63
  "author": "Michał Łaszczewski <michal@laszczewski.pl>",
63
64
  "license": "ISC",
64
65
  "description": "",
65
- "gitHead": "c967d5304bdac5150a6272c8d7cbe8974d7c25e6"
66
+ "gitHead": "ba8da813894eeb717223aa8d5e364649e4ac0347"
66
67
  }