Number input as Scale (#227)

* Number input as Scale

* scale input

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
formsdev
2023-10-26 18:28:35 +05:30
committed by GitHub
parent 8a2e071c56
commit 7c03d20cc4
5 changed files with 173 additions and 4 deletions

View File

@@ -0,0 +1,102 @@
<template>
<div :class="wrapperClass" :style="inputStyle">
<label v-if="label" :for="id?id:name"
:class="[theme.default.label,{'uppercase text-xs':uppercaseLabels, 'text-sm':!uppercaseLabels}]"
>
{{ label }}
<span v-if="required" class="text-red-500 required-dot">*</span>
</label>
<small v-if="help && helpPosition=='above_input'" :class="theme.default.help" class="flex mb-1">
<slot name="help"><span class="field-help" v-html="help" /></slot>
</small>
<div class="rectangle-outer grid grid-cols-5 gap-2">
<div v-for="i in scaleList" :key="i"
:class="[{'font-semibold':compVal===i},theme.ScaleInput.button, compVal!==i ? unselectedButtonClass: '']"
:style="btnStyle(i===compVal)"
role="button" @click="setScale(i)"
>
{{ i }}
</div>
</div>
<small v-if="help && helpPosition=='below_input'" :class="theme.default.help">
<slot name="help"><span class="field-help" v-html="help" /></slot>
</small>
<has-error v-if="hasValidation" :form="form" :field="name" />
</div>
</template>
<script>
import inputMixin from '~/mixins/forms/input.js'
export default {
name: 'ScaleInput',
mixins: [inputMixin],
props: {
minScale: { type: Number, default: 1 },
maxScale: { type: Number, default: 5 },
stepScale: { type: Number, default: 1 }
},
data () {
return {}
},
computed: {
scaleList () {
let list = []
for (let i = this.minScale; i <= this.maxScale; i += this.stepScale) {
list.push(i)
}
return list
},
unselectedButtonClass () {
return this.theme.ScaleInput.unselectedButton
},
textColor () {
const color = (this.color.charAt(0) === '#') ? this.color.substring(1, 7) : this.color
const r = parseInt(color.substring(0, 2), 16) // hexToR
const g = parseInt(color.substring(2, 4), 16) // hexToG
const b = parseInt(color.substring(4, 6), 16) // hexToB
const uicolors = [r / 255, g / 255, b / 255]
const c = uicolors.map((col) => {
if (col <= 0.03928) {
return col / 12.92
}
return Math.pow((col + 0.055) / 1.055, 2.4)
})
const L = (0.2126 * c[0]) + (0.7152 * c[1]) + (0.0722 * c[2])
return (L > 0.55) ? '#000000' : '#FFFFFF'
}
},
mounted () {
if (this.compVal && typeof this.compVal === 'string'){
this.compVal = parseInt(this.compVal)
}
},
methods: {
btnStyle (isSelected) {
if (!isSelected) return {}
return {
color: this.textColor,
backgroundColor: this.color
}
},
setScale (val) {
if (this.disabled) {
return
}
if (this.compVal === val) {
this.compVal = null
} else {
this.compVal = val
}
}
}
}
</script>

View File

@@ -15,6 +15,7 @@ import ImageInput from './ImageInput.vue'
import RatingInput from './RatingInput.vue'
import FlatSelectInput from './FlatSelectInput.vue'
import ToggleSwitchInput from './ToggleSwitchInput.vue'
import ScaleInput from './ScaleInput.vue'
// Components that are registered globaly.
[
@@ -32,7 +33,8 @@ import ToggleSwitchInput from './ToggleSwitchInput.vue'
ImageInput,
RatingInput,
FlatSelectInput,
ToggleSwitchInput
ToggleSwitchInput,
ScaleInput
].forEach(Component => {
Vue.component(Component.name, Component)
})

View File

@@ -150,6 +150,9 @@ export default {
if (field.type === 'number' && field.is_rating && field.rating_max_value) {
return 'RatingInput'
}
if (field.type === 'number' && field.is_scale && field.scale_max_value) {
return 'ScaleInput'
}
if (['select', 'multi_select'].includes(field.type) && field.without_dropdown) {
return 'FlatSelectInput'
}
@@ -302,6 +305,10 @@ export default {
inputProperties.accept = (this.form.is_pro && field.allowed_file_types) ? field.allowed_file_types : ""
} else if (field.type === 'number' && field.is_rating) {
inputProperties.numberOfStars = parseInt(field.rating_max_value)
} else if (field.type === 'number' && field.is_scale) {
inputProperties.minScale = parseInt(field.scale_min_value) ?? 1
inputProperties.maxScale = parseInt(field.scale_max_value) ?? 5
inputProperties.stepScale = parseInt(field.scale_step_value) ?? 1
} else if (field.type === 'number' || (field.type === 'phone_number' && field.use_simple_text_input)) {
inputProperties.pattern = '/\d*'
} else if (field.type === 'phone_number' && !field.use_simple_text_input) {

View File

@@ -73,13 +73,36 @@
Rating
</v-checkbox>
<p class="text-gray-400 mb-3 text-xs">
If enabled then this field will be star rating input.
Transform this field into a star rating input.
</p>
<text-input v-if="field.is_rating" name="rating_max_value" native-type="number" :min="1" class="mt-3"
:form="field" required
label="Max rating value"
/>
<v-checkbox v-model="field.is_scale" class="mt-4"
:name="field.id+'_is_scale'" @input="initScale"
>
Scale
</v-checkbox>
<p class="text-gray-400 text-xs mb-5">
Transform this field into a scale/score input.
</p>
<template v-if="field.is_scale">
<text-input name="scale_min_value" native-type="number" class="mt-4"
:form="field" required
label="Min scale value"
/>
<text-input name="scale_max_value" native-type="number" :min="1" class="mt-4"
:form="field" required
label="Max scale value"
/>
<text-input name="scale_step_value" native-type="number" :min="1" class="mt-4"
:form="field" required
label="Scale step svalue"
/>
</template>
</div>
<!-- Text Options -->
@@ -510,8 +533,25 @@ export default {
}
},
initRating() {
if (this.field.is_rating && !this.field.rating_max_value) {
this.$set(this.field, 'rating_max_value', 5)
if (this.field.is_rating) {
this.$set(this.field, 'is_scale', false)
if (!this.field.rating_max_value) {
this.$set(this.field, 'rating_max_value', 5)
}
}
},
initScale () {
if (this.field.is_scale) {
this.$set(this.field, 'is_rating', false)
if (!this.field.scale_min_value) {
this.$set(this.field, 'scale_min_value', 1)
}
if (!this.field.scale_max_value) {
this.$set(this.field, 'scale_max_value', 5)
}
if (!this.field.scale_step_value) {
this.$set(this.field, 'scale_step_value', 1)
}
}
},
onFieldOptionsChange(val) {