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) {
|
if (field.type === 'number' && field.is_scale && field.scale_max_value) {
|
||||||
return 'ScaleInput'
|
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) {
|
if (['select', 'multi_select'].includes(field.type) && field.without_dropdown) {
|
||||||
return 'FlatSelectInput'
|
return 'FlatSelectInput'
|
||||||
}
|
}
|
||||||
|
@ -308,6 +311,10 @@ export default {
|
||||||
inputProperties.minScale = parseInt(field.scale_min_value) ?? 1
|
inputProperties.minScale = parseInt(field.scale_min_value) ?? 1
|
||||||
inputProperties.maxScale = parseInt(field.scale_max_value) ?? 5
|
inputProperties.maxScale = parseInt(field.scale_max_value) ?? 5
|
||||||
inputProperties.stepScale = parseInt(field.scale_step_value) ?? 1
|
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)) {
|
} else if (field.type === 'number' || (field.type === 'phone_number' && field.use_simple_text_input)) {
|
||||||
inputProperties.pattern = '/\d*'
|
inputProperties.pattern = '/\d*'
|
||||||
} else if (field.type === 'phone_number' && !field.use_simple_text_input) {
|
} 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"
|
<text-input name="scale_step_value" native-type="number" :min="1" class="mt-4"
|
||||||
:form="field" required
|
: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>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -535,6 +558,7 @@ export default {
|
||||||
initRating () {
|
initRating () {
|
||||||
if (this.field.is_rating) {
|
if (this.field.is_rating) {
|
||||||
this.field.is_scale = false
|
this.field.is_scale = false
|
||||||
|
this.field.is_slider = false
|
||||||
if (!this.field.rating_max_value) {
|
if (!this.field.rating_max_value) {
|
||||||
this.field.rating_max_value = 5
|
this.field.rating_max_value = 5
|
||||||
}
|
}
|
||||||
|
@ -543,6 +567,7 @@ export default {
|
||||||
initScale () {
|
initScale () {
|
||||||
if (this.field.is_scale) {
|
if (this.field.is_scale) {
|
||||||
this.field.is_rating = false
|
this.field.is_rating = false
|
||||||
|
this.field.is_slider = false
|
||||||
if (!this.field.scale_min_value) {
|
if (!this.field.scale_min_value) {
|
||||||
this.field.scale_min_value = 1
|
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) {
|
onFieldOptionsChange (val) {
|
||||||
const vals = (val) ? val.trim().split('\n') : []
|
const vals = (val) ? val.trim().split('\n') : []
|
||||||
const tmpOpts = vals.map(name => {
|
const tmpOpts = vals.map(name => {
|
||||||
|
|
|
@ -32,6 +32,11 @@ export const themes = {
|
||||||
unselectedButton: 'bg-white hover:bg-gray-50 border',
|
unselectedButton: 'bg-white hover:bg-gray-50 border',
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
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: {
|
fileInput: {
|
||||||
input: 'min-h-40 border border-dashed border-gray-300 p-4 rounded-lg',
|
input: 'min-h-40 border border-dashed border-gray-300 p-4 rounded-lg',
|
||||||
inputHover: {
|
inputHover: {
|
||||||
|
@ -71,6 +76,11 @@ export const themes = {
|
||||||
unselectedButton: 'bg-white hover:bg-gray-50 border -mx-4',
|
unselectedButton: 'bg-white hover:bg-gray-50 border -mx-4',
|
||||||
help: 'text-gray-400 dark:text-gray-500'
|
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: {
|
fileInput: {
|
||||||
input: 'min-h-40 border border-dashed border-gray-300 p-4',
|
input: 'min-h-40 border border-dashed border-gray-300 p-4',
|
||||||
inputHover: {
|
inputHover: {
|
||||||
|
@ -110,6 +120,11 @@ export const themes = {
|
||||||
unselectedButton: 'bg-notion-input-background dark:bg-notion-dark-light hover:bg-gray-50 border',
|
unselectedButton: 'bg-notion-input-background dark:bg-notion-dark-light hover:bg-gray-50 border',
|
||||||
help: 'text-notion-input-help dark:text-gray-500'
|
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: {
|
fileInput: {
|
||||||
input: 'min-h-40 border border-dashed border-gray-300 p-4 rounded bg-notion-input-background',
|
input: 'min-h-40 border border-dashed border-gray-300 p-4 rounded bg-notion-input-background',
|
||||||
inputHover: {
|
inputHover: {
|
||||||
|
|
Loading…
Reference in New Issue