iview/src/components/slider/slider.vue

314 lines
12 KiB
Vue
Raw Normal View History

2016-10-28 17:35:28 +08:00
<template>
<div :class="classes">
<Input-number
v-if="!range && showInput"
:min="min"
:max="max"
:step="step"
2017-12-22 14:46:44 +01:00
:value="currentValue[0]"
:disabled="disabled"
@on-change="handleInputChange"></Input-number>
2017-03-03 15:52:19 +08:00
<div :class="[prefixCls + '-wrap']" ref="slider" @click.self="sliderClick">
2017-09-19 16:45:02 +08:00
<input type="hidden" :name="name" :value="currentValue">
2016-10-28 17:35:28 +08:00
<template v-if="showStops">
<div :class="[prefixCls + '-stop']" v-for="item in stops" :style="{ 'left': item + '%' }" @click.self="sliderClick"></div>
2016-10-28 17:35:28 +08:00
</template>
<div :class="[prefixCls + '-bar']" :style="barStyle" @click.self="sliderClick"></div>
2017-11-11 10:16:43 +01:00
<div
:class="[prefixCls + '-button-wrap']"
:style="{left: minPosition + '%'}"
@touchstart="onPointerDown($event, 'min')"
@mousedown="onPointerDown($event, 'min')">
<Tooltip :controlled="pointerDown === 'min'" placement="top" :content="tipFormat(currentValue[0])"
:disabled="tipDisabled" :always="showTip === 'always'" ref="minTooltip">
<div :class="minButtonClasses"></div>
</Tooltip>
</div>
<div v-if="range"
:class="[prefixCls + '-button-wrap']"
:style="{left: maxPosition + '%'}"
@touchstart="onPointerDown($event, 'max')"
@mousedown="onPointerDown($event, 'max')">
<Tooltip :controlled="pointerDown === 'max'" placement="top" :content="tipFormat(currentValue[1])"
:disabled="tipDisabled" :always="showTip === 'always'" ref="maxTooltip">
<div :class="maxButtonClasses"></div>
</Tooltip>
</div>
2016-10-28 17:35:28 +08:00
</div>
</div>
</template>
<script>
import InputNumber from '../../components/input-number/input-number.vue';
import Tooltip from '../../components/tooltip/tooltip.vue';
import { getStyle, oneOf } from '../../utils/assist';
2017-07-10 13:52:53 +08:00
import { on, off } from '../../utils/dom';
import Emitter from '../../mixins/emitter';
2016-10-28 17:35:28 +08:00
const prefixCls = 'ivu-slider';
export default {
2017-03-03 17:46:09 +08:00
name: 'Slider',
mixins: [ Emitter ],
2016-10-28 17:35:28 +08:00
components: { InputNumber, Tooltip },
props: {
min: {
type: Number,
default: 0
},
max: {
type: Number,
default: 100
},
step: {
type: Number,
default: 1
},
range: {
type: Boolean,
default: false
},
value: {
type: [Number, Array],
default: 0
},
disabled: {
type: Boolean,
default: false
},
showInput: {
type: Boolean,
default: false
},
showStops: {
type: Boolean,
default: false
},
tipFormat: {
type: Function,
default (val) {
return val;
}
},
showTip: {
type: String,
default: 'hover',
validator (value) {
return oneOf(value, ['hover', 'always', 'never']);
}
2017-09-19 16:45:02 +08:00
},
name: {
type: String
2016-10-28 17:35:28 +08:00
}
},
data () {
2017-11-11 10:16:43 +01:00
const val = this.checkLimits(Array.isArray(this.value) ? this.value : [this.value]);
2016-10-28 17:35:28 +08:00
return {
prefixCls: prefixCls,
2017-11-11 10:16:43 +01:00
currentValue: val,
dragging: false,
2017-11-11 10:16:43 +01:00
pointerDown: '',
startX: 0,
currentX: 0,
startPos: 0,
2017-11-11 10:16:43 +01:00
oldValue: val
2016-12-25 22:49:42 +08:00
};
2016-10-28 17:35:28 +08:00
},
2017-03-03 15:52:19 +08:00
watch: {
value (val) {
2017-11-11 10:16:43 +01:00
val = this.checkLimits(Array.isArray(val) ? val : [val]);
if (val[0] !== this.currentValue[0] || val[1] !== this.currentValue[1]) {
this.currentValue = val;
}
2017-03-03 15:52:19 +08:00
},
currentValue (val) {
this.$nextTick(() => {
2017-11-11 10:16:43 +01:00
this.$refs.minTooltip.updatePopper();
2017-03-03 15:52:19 +08:00
if (this.range) {
2017-11-11 10:16:43 +01:00
this.$refs.maxTooltip.updatePopper();
2017-03-03 15:52:19 +08:00
}
});
2017-11-11 10:16:43 +01:00
const exportValue = this.range ? val : val[0];
this.$emit('input', exportValue);
this.$emit('on-input', exportValue);
2017-03-03 15:52:19 +08:00
}
},
2016-10-28 17:35:28 +08:00
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-input`]: this.showInput && !this.range,
2016-10-28 17:35:28 +08:00
[`${prefixCls}-range`]: this.range,
[`${prefixCls}-disabled`]: this.disabled
}
2016-12-25 22:49:42 +08:00
];
2016-10-28 17:35:28 +08:00
},
2017-11-11 10:16:43 +01:00
minButtonClasses () {
return [
`${prefixCls}-button`,
{
2017-11-11 10:16:43 +01:00
[`${prefixCls}-button-dragging`]: this.pointerDown === 'min'
}
];
},
2017-11-11 10:16:43 +01:00
maxButtonClasses () {
return [
`${prefixCls}-button`,
{
2017-11-11 10:16:43 +01:00
[`${prefixCls}-button-dragging`]: this.pointerDown === 'max'
}
];
},
2017-11-11 10:16:43 +01:00
minPosition () {
const val = this.currentValue;
return (val[0] - this.min) / this.valueRange * 100;
2017-11-11 10:16:43 +01:00
},
maxPosition: function () {
const val = this.currentValue;
return (val[1] - this.min) / this.valueRange * 100;
},
2016-10-28 17:35:28 +08:00
barStyle () {
2018-01-09 06:07:23 +01:00
2017-11-11 10:16:43 +01:00
const style = {
width: (this.currentValue[0] - this.min) / this.valueRange * 100 + '%'
2017-11-11 10:16:43 +01:00
};
2016-10-28 17:35:28 +08:00
if (this.range) {
style.left = (this.currentValue[0] - this.min) / this.valueRange * 100 + '%';
style.width = (this.currentValue[1] - this.currentValue[0]) / this.valueRange * 100 + '%';
2016-10-28 17:35:28 +08:00
}
return style;
},
2017-11-11 10:16:43 +01:00
stops () {
let stopCount = this.valueRange / this.step;
let result = [];
let stepWidth = 100 * this.step / this.valueRange;
for (let i = 1; i < stopCount; i++) {
result.push(i * stepWidth);
}
return result;
},
sliderWidth () {
2017-03-03 15:52:19 +08:00
return parseInt(getStyle(this.$refs.slider, 'width'), 10);
},
tipDisabled () {
2017-03-03 15:52:19 +08:00
return this.tipFormat(this.currentValue[0]) === null || this.showTip === 'never';
},
valueRange(){
return this.max - this.min;
2016-10-28 17:35:28 +08:00
}
},
methods: {
2017-11-11 10:16:43 +01:00
getPointerX (e) {
return e.type.indexOf('touch') !== -1 ? e.touches[0].clientX : e.clientX;
},
2017-11-11 10:16:43 +01:00
checkLimits ([min, max]) {
min = Math.max(this.min, min);
min = Math.min(this.max, min);
2016-10-28 17:35:28 +08:00
max = Math.max(this.min, min, max);
max = Math.min(this.max, max);
2017-11-11 10:16:43 +01:00
return [min, max];
},
2017-11-11 10:16:43 +01:00
onPointerDown (event, type) {
if (this.disabled) return;
event.preventDefault();
2017-11-11 10:16:43 +01:00
this.pointerDown = type;
this.onPointerDragStart(event);
on(window, 'mousemove', this.onPointerDrag);
on(window, 'touchmove', this.onPointerDrag);
on(window, 'mouseup', this.onPointerDragEnd);
on(window, 'touchend', this.onPointerDragEnd);
},
2017-11-11 10:16:43 +01:00
onPointerDragStart (event) {
this.dragging = false;
2017-11-11 10:16:43 +01:00
this.startX = this.getPointerX(event);
this.startPos = (this[`${this.pointerDown}Position`] * this.valueRange / 100) + this.min;
},
2017-11-11 10:16:43 +01:00
onPointerDrag (event) {
this.dragging = true;
2017-11-11 10:16:43 +01:00
this.$refs[`${this.pointerDown}Tooltip`].visible = true;
this.currentX = this.getPointerX(event);
const diff = (this.currentX - this.startX) / this.sliderWidth * this.valueRange;
2017-11-11 10:16:43 +01:00
this.changeButtonPosition(this.startPos + diff);
},
2017-11-11 10:16:43 +01:00
onPointerDragEnd () {
if (this.dragging) {
this.dragging = false;
2017-11-11 10:16:43 +01:00
this.$refs[`${this.pointerDown}Tooltip`].visible = false;
2018-02-02 07:55:09 +01:00
this.emitChange();
}
2017-11-11 10:16:43 +01:00
this.pointerDown = '';
off(window, 'mousemove', this.onPointerDrag);
off(window, 'touchmove', this.onPointerDrag);
off(window, 'mouseup', this.onPointerDragEnd);
off(window, 'touchend', this.onPointerDragEnd);
},
2017-11-11 10:16:43 +01:00
changeButtonPosition (newPos, forceType) {
const type = forceType || this.pointerDown;
const index = type === 'min' ? 0 : 1;
if (type === 'min') newPos = this.checkLimits([newPos, this.maxPosition])[0];
else newPos = this.checkLimits([this.minPosition, newPos])[1];
2018-01-30 10:24:29 +01:00
const modulus = newPos % this.step;
2017-11-11 10:16:43 +01:00
const value = this.currentValue;
2018-01-30 10:24:29 +01:00
value[index] = newPos - modulus;
2017-11-11 10:16:43 +01:00
this.currentValue = [...value];
2017-04-05 12:31:49 +08:00
if (!this.dragging) {
2017-11-11 10:16:43 +01:00
if (this.currentValue[index] !== this.oldValue[index]) {
2018-02-02 07:55:09 +01:00
this.emitChange();
2017-11-11 10:16:43 +01:00
this.oldValue[index] = this.currentValue[index];
}
}
},
2018-02-02 07:55:09 +01:00
emitChange(){
const exportValue = this.range ? this.currentValue : this.currentValue[0];
this.$emit('on-change', exportValue);
this.dispatch('FormItem', 'on-form-change', exportValue);
},
2018-01-23 18:14:23 +08:00
sliderClick (event) {
if (this.disabled) return;
2017-11-11 10:16:43 +01:00
const currentX = this.getPointerX(event);
const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
let newPos = ((currentX - sliderOffsetLeft) / this.sliderWidth * this.valueRange) + this.min;
2017-11-11 10:16:43 +01:00
if (!this.range || newPos <= this.minPosition) this.changeButtonPosition(newPos, 'min');
else if (newPos >= this.maxPosition) this.changeButtonPosition(newPos, 'max');
else this.changeButtonPosition(newPos, ((newPos - this.firstPosition) <= (this.secondPosition - newPos)) ? 'min' : 'max');
},
2017-11-11 10:16:43 +01:00
handleInputChange (val) {
this.currentValue = [val, this.currentValue[1]];
const exportValue = this.range ? this.currentValue : this.currentValue[0];
this.$emit('on-change', exportValue);
this.dispatch('FormItem', 'on-form-change', exportValue);
},
2018-01-23 18:14:23 +08:00
},
mounted () {
// #2852
this.$on('on-visible-change', (val) => {
if (val && this.showTip === 'always') {
this.$refs.minTooltip.doDestroy();
if (this.range) {
this.$refs.maxTooltip.doDestroy();
}
this.$nextTick(() => {
this.$refs.minTooltip.updatePopper();
if (this.range) {
this.$refs.maxTooltip.updatePopper();
}
});
}
});
2016-10-28 17:35:28 +08:00
}
2016-12-25 22:49:42 +08:00
};
</script>