Input Slider Feature (#292)
* feat: input slider feature * Polish slider label --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
2ac5f56a93
commit
0c88c9842a
|
@ -0,0 +1,100 @@
|
|||
<template>
|
||||
<input-wrapper v-bind="inputWrapperProps">
|
||||
<template #label>
|
||||
<slot name="label" />
|
||||
</template>
|
||||
<div class="flex space-x-2">
|
||||
<div class="flex-1 relative">
|
||||
<div class="font-medium text-sm absolute -top-[6px]"
|
||||
:style="labelStyle"
|
||||
>
|
||||
<div class="">
|
||||
{{ compVal }}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
class="w-full mt-3"
|
||||
:min="minSlider"
|
||||
:max="maxSlider"
|
||||
:step="stepSlider"
|
||||
v-model="compVal"
|
||||
/>
|
||||
<div class="grid grid-cols-3 gap-2 -mt-1">
|
||||
<div
|
||||
v-for="i in sliderLabelsList"
|
||||
:key="i"
|
||||
:class="[
|
||||
theme.SliderInput.stepLabel,
|
||||
i.style,
|
||||
]"
|
||||
>
|
||||
{{ i.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #help>
|
||||
<slot name="help" />
|
||||
</template>
|
||||
<template #error>
|
||||
<slot name="error" />
|
||||
</template>
|
||||
</input-wrapper>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inputProps, useFormInput } from "./useFormInput.js";
|
||||
import InputWrapper from "./components/InputWrapper.vue";
|
||||
|
||||
export default {
|
||||
name: "SliderInput",
|
||||
components: { InputWrapper },
|
||||
|
||||
props: {
|
||||
...inputProps,
|
||||
minSlider: { type: Number, default: 0 },
|
||||
maxSlider: { type: Number, default: 50 },
|
||||
stepSlider: { type: Number, default: 5 },
|
||||
},
|
||||
|
||||
setup(props, context) {
|
||||
return {
|
||||
...useFormInput(props, context),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
labelStyle() {
|
||||
const ratio = ((this.compVal-this.minSlider) / (this.maxSlider-this.minSlider)) * 100
|
||||
return {
|
||||
left: `${ratio}%`,
|
||||
marginLeft: `-${ratio/100*15}px`
|
||||
}
|
||||
},
|
||||
sliderLabelsList() {
|
||||
const midPoint = (this.maxSlider - this.minSlider) / 2 + this.minSlider;
|
||||
const labels = [
|
||||
{
|
||||
label: `${this.minSlider}`,
|
||||
style: "flex items-center justify-start",
|
||||
},
|
||||
{
|
||||
label: Math.floor(midPoint),
|
||||
style: "flex items-center justify-center",
|
||||
},
|
||||
{
|
||||
label: `${this.maxSlider}`,
|
||||
style: "flex items-center justify-end",
|
||||
},
|
||||
];
|
||||
return labels;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.compVal = parseInt(this.compVal ?? this.minSlider);
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
</script>
|
|
@ -141,6 +141,9 @@ export default {
|
|||
if (field.type === 'number' && field.is_scale && field.scale_max_value) {
|
||||
return 'ScaleInput'
|
||||
}
|
||||
if (field.type === 'number' && field.is_slider && field.slider_max_value) {
|
||||
return 'SliderInput'
|
||||
}
|
||||
if (['select', 'multi_select'].includes(field.type) && field.without_dropdown) {
|
||||
return 'FlatSelectInput'
|
||||
}
|
||||
|
@ -308,6 +311,10 @@ export default {
|
|||
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.is_slider) {
|
||||
inputProperties.minSlider = parseInt(field.slider_min_value) ?? 0
|
||||
inputProperties.maxSlider = parseInt(field.slider_max_value) ?? 50
|
||||
inputProperties.stepSlider = parseInt(field.slider_step_value) ?? 5
|
||||
} 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) {
|
||||
|
|
|
@ -100,7 +100,30 @@
|
|||
/>
|
||||
<text-input name="scale_step_value" native-type="number" :min="1" class="mt-4"
|
||||
:form="field" required
|
||||
label="Scale step svalue"
|
||||
label="Scale steps value"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<v-checkbox v-model="field.is_slider" class="mt-4"
|
||||
:name="field.id+'_is_slider'" @update:model-value="initSlider"
|
||||
>
|
||||
Slider
|
||||
</v-checkbox>
|
||||
<p class="text-gray-400 text-xs mb-5">
|
||||
Transform this field into a slider input.
|
||||
</p>
|
||||
<template v-if="field.is_slider">
|
||||
<text-input name="slider_min_value" native-type="number" class="mt-4"
|
||||
:form="field" required
|
||||
label="Min slider value"
|
||||
/>
|
||||
<text-input name="slider_max_value" native-type="number" :min="1" class="mt-4"
|
||||
:form="field" required
|
||||
label="Max slider value"
|
||||
/>
|
||||
<text-input name="slider_step_value" native-type="number" :min="1" class="mt-4"
|
||||
:form="field" required
|
||||
label="Slider steps value"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
@ -535,6 +558,7 @@ export default {
|
|||
initRating () {
|
||||
if (this.field.is_rating) {
|
||||
this.field.is_scale = false
|
||||
this.field.is_slider = false
|
||||
if (!this.field.rating_max_value) {
|
||||
this.field.rating_max_value = 5
|
||||
}
|
||||
|
@ -543,6 +567,7 @@ export default {
|
|||
initScale () {
|
||||
if (this.field.is_scale) {
|
||||
this.field.is_rating = false
|
||||
this.field.is_slider = false
|
||||
if (!this.field.scale_min_value) {
|
||||
this.field.scale_min_value = 1
|
||||
}
|
||||
|
@ -554,6 +579,22 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
initSlider () {
|
||||
if (this.field.is_slider) {
|
||||
this.field.is_rating = false
|
||||
this.field.is_scale = false
|
||||
if (!this.field.slider_min_value) {
|
||||
this.field.slider_min_value = 0
|
||||
}
|
||||
if (!this.field.slider_max_value) {
|
||||
this.field.slider_max_value = 50
|
||||
}
|
||||
if (!this.field.slider_step_value) {
|
||||
this.field.slider_step_value = 1
|
||||
}
|
||||
}
|
||||
},
|
||||
onFieldOptionsChange (val) {
|
||||
const vals = (val) ? val.trim().split('\n') : []
|
||||
const tmpOpts = vals.map(name => {
|
||||
|
|
|
@ -32,6 +32,11 @@ export const themes = {
|
|||
unselectedButton: 'bg-white hover:bg-gray-50 border',
|
||||
help: 'text-gray-400 dark:text-gray-500'
|
||||
},
|
||||
SliderInput: {
|
||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
||||
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs',
|
||||
help: 'text-gray-400 dark:text-gray-500'
|
||||
},
|
||||
fileInput: {
|
||||
input: 'min-h-40 border border-dashed border-gray-300 p-4 rounded-lg',
|
||||
inputHover: {
|
||||
|
@ -71,6 +76,11 @@ export const themes = {
|
|||
unselectedButton: 'bg-white hover:bg-gray-50 border -mx-4',
|
||||
help: 'text-gray-400 dark:text-gray-500'
|
||||
},
|
||||
SliderInput: {
|
||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
||||
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs',
|
||||
help: 'text-gray-400 dark:text-gray-500'
|
||||
},
|
||||
fileInput: {
|
||||
input: 'min-h-40 border border-dashed border-gray-300 p-4',
|
||||
inputHover: {
|
||||
|
@ -110,6 +120,11 @@ export const themes = {
|
|||
unselectedButton: 'bg-notion-input-background dark:bg-notion-dark-light hover:bg-gray-50 border',
|
||||
help: 'text-notion-input-help dark:text-gray-500'
|
||||
},
|
||||
SliderInput: {
|
||||
label: 'text-gray-700 dark:text-gray-300 font-semibold',
|
||||
stepLabel: 'text-gray-700 dark:text-gray-300 text-center text-xs',
|
||||
help: 'text-gray-400 dark:text-gray-500'
|
||||
},
|
||||
fileInput: {
|
||||
input: 'min-h-40 border border-dashed border-gray-300 p-4 rounded bg-notion-input-background',
|
||||
inputHover: {
|
||||
|
|
Loading…
Reference in New Issue