2018-01-31 01:21:21 +03:00
|
|
|
<template>
|
2018-02-01 01:20:29 +03:00
|
|
|
<div :class="classes">
|
2018-01-31 01:21:21 +03:00
|
|
|
<div class="formulate-element-input-wrapper">
|
|
|
|
<!-- TEXT STYLE INPUTS -->
|
|
|
|
<label
|
|
|
|
:for="id"
|
|
|
|
v-text="label"
|
2018-02-01 01:20:29 +03:00
|
|
|
v-if="label && (!isBoxInput || optionList.length > 1)"
|
2018-01-31 01:21:21 +03:00
|
|
|
/>
|
|
|
|
<input
|
|
|
|
ref="input"
|
|
|
|
:type="type"
|
|
|
|
:name="name"
|
|
|
|
v-model="val"
|
2018-02-01 01:20:29 +03:00
|
|
|
v-bind="attributes"
|
2018-01-31 01:21:21 +03:00
|
|
|
v-if="isTextInput"
|
2018-02-01 01:20:29 +03:00
|
|
|
@blur="errorBlurState = true"
|
2018-01-31 01:21:21 +03:00
|
|
|
>
|
|
|
|
<!-- BUTTON INPUTS -->
|
|
|
|
<button
|
|
|
|
:type="type"
|
|
|
|
v-text="label || name"
|
|
|
|
v-if="isButtonInput"
|
2018-02-01 01:20:29 +03:00
|
|
|
:disabled="type === 'submit' && (form.hasErrors && form.behavior === 'live')"
|
2018-01-31 01:21:21 +03:00
|
|
|
/>
|
|
|
|
<!-- SELECT INPUTS -->
|
2018-02-01 01:20:29 +03:00
|
|
|
<select
|
|
|
|
v-bind="attributes"
|
|
|
|
v-if="isSelectInput"
|
|
|
|
:name="name"
|
|
|
|
v-model="val"
|
|
|
|
@blur="errorBlurState = true"
|
|
|
|
>
|
|
|
|
<option
|
|
|
|
v-for="option in optionList"
|
|
|
|
:value="option.value"
|
|
|
|
:key="option.id"
|
2018-02-07 23:51:47 +03:00
|
|
|
v-bind="option.attributes || {}"
|
2018-02-01 01:20:29 +03:00
|
|
|
v-text="option.label"
|
|
|
|
/>
|
|
|
|
</select>
|
|
|
|
<!-- BOX INPUTS -->
|
|
|
|
<div
|
|
|
|
class="formulate-element-box-input-group"
|
|
|
|
v-if="isBoxInput"
|
|
|
|
>
|
|
|
|
<template v-for="option in optionList">
|
|
|
|
<input
|
|
|
|
type="radio"
|
|
|
|
:name="name"
|
|
|
|
:id="option.id"
|
|
|
|
:value="option.value"
|
|
|
|
:key="`${option.id}-input`"
|
|
|
|
v-bind="attributes"
|
|
|
|
v-model="val"
|
|
|
|
v-if="type === 'radio'"
|
|
|
|
@blur="errorBlurState = true"
|
|
|
|
>
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
:name="name"
|
|
|
|
:id="option.id"
|
|
|
|
:value="option.value"
|
|
|
|
:key="`${option.id}-input`"
|
|
|
|
v-bind="attributes"
|
|
|
|
v-model="val"
|
|
|
|
v-if="type === 'checkbox'"
|
|
|
|
@blur="errorBlurState = true"
|
|
|
|
>
|
|
|
|
<label
|
|
|
|
:for="option.id"
|
|
|
|
:key="`${option.id}-label`"
|
|
|
|
v-text="option.label"
|
|
|
|
/>
|
|
|
|
</template>
|
|
|
|
</div>
|
2018-01-31 01:21:21 +03:00
|
|
|
<!-- CUSTOM SLOT INPUTS -->
|
|
|
|
<slot v-if="hasCustomInput" />
|
2018-02-01 01:20:29 +03:00
|
|
|
|
|
|
|
<!-- UNSUPORTED INPUT -->
|
|
|
|
<div
|
|
|
|
style="background-color: red; color: white"
|
|
|
|
v-if="isUnsupportedInput"
|
|
|
|
v-text="`Unsupported field type: “${type}”.`"
|
|
|
|
/>
|
2018-01-31 01:21:21 +03:00
|
|
|
</div>
|
|
|
|
<ul
|
|
|
|
class="formulate-errors"
|
2018-02-01 01:20:29 +03:00
|
|
|
v-if="shouldShowErrors && localAndValidationErrors.length"
|
2018-01-31 01:21:21 +03:00
|
|
|
>
|
|
|
|
<li
|
|
|
|
v-for="error in localAndValidationErrors"
|
|
|
|
v-text="error"
|
|
|
|
:key="error"
|
|
|
|
/>
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2018-02-02 20:07:51 +03:00
|
|
|
import {inputTypes, equals, reduce, filter} from '../utils'
|
2018-01-31 01:21:21 +03:00
|
|
|
import shortid from 'shortid'
|
|
|
|
|
|
|
|
export default {
|
|
|
|
props: {
|
|
|
|
type: {
|
|
|
|
type: [String, Boolean],
|
|
|
|
default: 'text'
|
|
|
|
},
|
|
|
|
name: {
|
|
|
|
type: String,
|
|
|
|
required: true
|
|
|
|
},
|
|
|
|
initial: {
|
|
|
|
type: [String, Boolean],
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
validation: {
|
|
|
|
type: [String, Boolean],
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
errors: {
|
|
|
|
type: Array,
|
|
|
|
default: () => []
|
|
|
|
},
|
|
|
|
label: {
|
|
|
|
type: [String, Boolean],
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
id: {
|
|
|
|
type: [String],
|
|
|
|
default: () => shortid.generate()
|
2018-02-01 01:20:29 +03:00
|
|
|
},
|
|
|
|
min: {
|
|
|
|
type: [String, Number, Boolean],
|
|
|
|
default: () => false
|
|
|
|
},
|
|
|
|
max: {
|
|
|
|
type: [String, Number, Boolean],
|
|
|
|
default: () => false
|
|
|
|
},
|
|
|
|
placeholder: {
|
|
|
|
type: [String, Number, Boolean],
|
|
|
|
default: () => false
|
|
|
|
},
|
|
|
|
options: {
|
|
|
|
type: [Object, Array],
|
|
|
|
default: () => []
|
|
|
|
},
|
|
|
|
multiple: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
showErrors: {
|
|
|
|
type: [Object, Boolean],
|
|
|
|
default: () => ({})
|
2018-02-02 21:38:05 +03:00
|
|
|
},
|
|
|
|
validationLabel: {
|
|
|
|
type: [String, Boolean],
|
|
|
|
default: false
|
2018-02-01 01:20:29 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
data () {
|
|
|
|
return {
|
|
|
|
errorBlurState: false
|
2018-01-31 01:21:21 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
hasCustomInput () {
|
|
|
|
return (this.$slots.default && this.$slots.default.length)
|
|
|
|
},
|
|
|
|
isTextInput () {
|
|
|
|
return !this.hasCustomInput && inputTypes.text.includes(this.type)
|
|
|
|
},
|
|
|
|
isButtonInput () {
|
|
|
|
return !this.hasCustomInput && inputTypes.button.includes(this.type)
|
|
|
|
},
|
2018-02-01 01:20:29 +03:00
|
|
|
isSelectInput () {
|
|
|
|
return !this.hasCustomInput && inputTypes.select.includes(this.type)
|
|
|
|
},
|
|
|
|
isBoxInput () {
|
|
|
|
return !this.hasCustomInput && inputTypes.box.includes(this.type)
|
|
|
|
},
|
|
|
|
isUnsupportedInput () {
|
|
|
|
return (!this.hasCustomInput && !this.isTextInput && !this.isButtonInput && !this.isSelectInput && !this.isBoxInput)
|
2018-01-31 01:21:21 +03:00
|
|
|
},
|
|
|
|
form () {
|
|
|
|
let parent = this.$parent
|
|
|
|
while (parent && parent.$data && parent.$data.parentIdentifier !== 'vue-formulate-wrapper-element') {
|
|
|
|
parent = parent.$parent
|
|
|
|
}
|
|
|
|
if (!parent.$data || parent.$data.parentIdentifier !== 'vue-formulate-wrapper-element') {
|
|
|
|
throw new Error('FormulateElement has no FormulateWrapper element')
|
|
|
|
}
|
|
|
|
return parent
|
|
|
|
},
|
|
|
|
values () {
|
|
|
|
return this.form.values
|
|
|
|
},
|
|
|
|
value () {
|
2018-02-01 01:20:29 +03:00
|
|
|
let value = this.values[this.name]
|
|
|
|
if (value === undefined) {
|
|
|
|
switch (this.type) {
|
|
|
|
case 'color':
|
2018-02-02 20:07:51 +03:00
|
|
|
return '#000000'
|
2018-02-01 01:20:29 +03:00
|
|
|
case 'checkbox':
|
|
|
|
if (this.optionList.length > 1) {
|
2018-02-02 20:07:51 +03:00
|
|
|
return []
|
2018-02-01 01:20:29 +03:00
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value
|
2018-01-31 01:21:21 +03:00
|
|
|
},
|
|
|
|
module () {
|
|
|
|
return this.form.$props['module']
|
|
|
|
},
|
|
|
|
formName () {
|
|
|
|
return this.form.$props['name']
|
|
|
|
},
|
2018-02-01 01:20:29 +03:00
|
|
|
classes () {
|
|
|
|
return {
|
|
|
|
'formulate-element': true,
|
|
|
|
'formulate-element--has-value': !!this.value,
|
|
|
|
'formulate-element--has-errors': this.localAndValidationErrors.length && this.shouldShowErrors
|
|
|
|
}
|
|
|
|
},
|
2018-01-31 01:21:21 +03:00
|
|
|
validationErrors () {
|
|
|
|
return this.form.validationErrors[this.name] || []
|
|
|
|
},
|
|
|
|
storeErrors () {
|
|
|
|
return this.form.errors[this.name] || []
|
|
|
|
},
|
|
|
|
localAndValidationErrors () {
|
|
|
|
return this.errors.concat(this.validationErrors)
|
|
|
|
},
|
2018-02-01 01:20:29 +03:00
|
|
|
shouldShowErrors () {
|
|
|
|
let show = this.form.shouldShowErrors
|
|
|
|
if (this.form.behavior === 'blur') {
|
2018-02-01 19:02:58 +03:00
|
|
|
show = show || this.errorBlurState
|
2018-02-01 01:20:29 +03:00
|
|
|
}
|
|
|
|
if (this.showErrors === false || this.showErrors === true) {
|
|
|
|
show = this.showErrors
|
|
|
|
}
|
|
|
|
return show
|
|
|
|
},
|
2018-01-31 01:21:21 +03:00
|
|
|
attributes () {
|
2018-02-01 01:20:29 +03:00
|
|
|
return ['min', 'max', 'placeholder', 'id', 'multiple']
|
|
|
|
.filter(prop => this[prop] !== false)
|
|
|
|
.reduce((attributes, attr) => {
|
|
|
|
attributes[attr] = this[attr]
|
|
|
|
return attributes
|
|
|
|
}, {})
|
|
|
|
},
|
|
|
|
optionList () {
|
|
|
|
if (!Array.isArray(this.options)) {
|
|
|
|
return reduce(this.options, (options, value, label) => options.concat({value, label, id: shortid.generate()}), [])
|
|
|
|
} else if (Array.isArray(this.options) && !this.options.length) {
|
|
|
|
return [{value: this.name, label: (this.label || this.name), id: shortid.generate()}]
|
|
|
|
}
|
|
|
|
return this.options
|
2018-01-31 01:21:21 +03:00
|
|
|
},
|
|
|
|
val: {
|
|
|
|
set (value) {
|
|
|
|
this.form.update({field: this.name, value})
|
2018-02-01 01:20:29 +03:00
|
|
|
if (this.isTextInput) {
|
|
|
|
this.$refs.input.value = value
|
|
|
|
}
|
2018-01-31 01:21:21 +03:00
|
|
|
},
|
|
|
|
get () {
|
|
|
|
return this.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
2018-02-01 01:20:29 +03:00
|
|
|
localAndValidationErrors () {
|
|
|
|
if (!equals(this.localAndValidationErrors, this.storeErrors)) {
|
|
|
|
this.form.updateFieldErrors({
|
|
|
|
field: this.name,
|
|
|
|
errors: this.localAndValidationErrors
|
|
|
|
})
|
|
|
|
}
|
2018-02-05 22:17:26 +03:00
|
|
|
},
|
|
|
|
initial () {
|
|
|
|
this.form.update({field: this.name, value: this.initial})
|
2018-01-31 01:21:21 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
created () {
|
2018-02-02 20:07:51 +03:00
|
|
|
this.form.registerField(
|
|
|
|
this.name,
|
2018-02-02 20:08:21 +03:00
|
|
|
filter(this.$props, (prop, value) => ['name', 'type', 'id', 'label', 'validation', 'validationLabel'].includes(prop))
|
2018-02-02 20:07:51 +03:00
|
|
|
)
|
2018-01-31 01:21:21 +03:00
|
|
|
if (this.initial !== false) {
|
|
|
|
this.form.hydrate({[this.name]: this.initial})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|