mirror of https://github.com/ElemeFE/element
388 lines
10 KiB
Vue
388 lines
10 KiB
Vue
<template>
|
|
<div
|
|
class="el-slider"
|
|
:class="{ 'is-vertical': vertical, 'el-slider--with-input': showInput }"
|
|
role="slider"
|
|
:aria-valuemin="min"
|
|
:aria-valuemax="max"
|
|
:aria-orientation="vertical ? 'vertical': 'horizontal'"
|
|
:aria-disabled="sliderDisabled"
|
|
>
|
|
<el-input-number
|
|
v-model="firstValue"
|
|
v-if="showInput && !range"
|
|
class="el-slider__input"
|
|
ref="input"
|
|
@change="$nextTick(emitChange)"
|
|
:step="step"
|
|
:disabled="sliderDisabled"
|
|
:controls="showInputControls"
|
|
:min="min"
|
|
:max="max"
|
|
:debounce="debounce"
|
|
:size="inputSize">
|
|
</el-input-number>
|
|
<div
|
|
class="el-slider__runway"
|
|
:class="{ 'show-input': showInput, 'disabled': sliderDisabled }"
|
|
:style="runwayStyle"
|
|
@click="onSliderClick"
|
|
ref="slider">
|
|
<div
|
|
class="el-slider__bar"
|
|
:style="barStyle">
|
|
</div>
|
|
<slider-button
|
|
:vertical="vertical"
|
|
v-model="firstValue"
|
|
:tooltip-class="tooltipClass"
|
|
ref="button1">
|
|
</slider-button>
|
|
<slider-button
|
|
:vertical="vertical"
|
|
v-model="secondValue"
|
|
:tooltip-class="tooltipClass"
|
|
ref="button2"
|
|
v-if="range">
|
|
</slider-button>
|
|
<div
|
|
class="el-slider__stop"
|
|
v-for="(item, key) in stops"
|
|
:key="key"
|
|
:style="vertical ? { 'bottom': item + '%' } : { 'left': item + '%' }"
|
|
v-if="showStops">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script type="text/babel">
|
|
import ElInputNumber from 'element-ui/packages/input-number';
|
|
import SliderButton from './button.vue';
|
|
import Emitter from 'element-ui/src/mixins/emitter';
|
|
|
|
export default {
|
|
name: 'ElSlider',
|
|
|
|
mixins: [Emitter],
|
|
|
|
inject: {
|
|
elForm: {
|
|
default: ''
|
|
}
|
|
},
|
|
|
|
props: {
|
|
min: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
max: {
|
|
type: Number,
|
|
default: 100
|
|
},
|
|
step: {
|
|
type: Number,
|
|
default: 1
|
|
},
|
|
value: {
|
|
type: [Number, Array],
|
|
default: 0
|
|
},
|
|
showInput: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
showInputControls: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
inputSize: {
|
|
type: String,
|
|
default: 'small'
|
|
},
|
|
showStops: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
showTooltip: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
formatTooltip: Function,
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
range: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
vertical: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
height: {
|
|
type: String
|
|
},
|
|
debounce: {
|
|
type: Number,
|
|
default: 300
|
|
},
|
|
label: {
|
|
type: String
|
|
},
|
|
tooltipClass: String
|
|
},
|
|
|
|
components: {
|
|
ElInputNumber,
|
|
SliderButton
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
firstValue: null,
|
|
secondValue: null,
|
|
oldValue: null,
|
|
dragging: false,
|
|
sliderSize: 1
|
|
};
|
|
},
|
|
|
|
watch: {
|
|
value(val, oldVal) {
|
|
if (this.dragging ||
|
|
Array.isArray(val) &&
|
|
Array.isArray(oldVal) &&
|
|
val.every((item, index) => item === oldVal[index])) {
|
|
return;
|
|
}
|
|
this.setValues();
|
|
},
|
|
|
|
dragging(val) {
|
|
if (!val) {
|
|
this.setValues();
|
|
}
|
|
},
|
|
|
|
firstValue(val) {
|
|
if (this.range) {
|
|
this.$emit('input', [this.minValue, this.maxValue]);
|
|
} else {
|
|
this.$emit('input', val);
|
|
}
|
|
},
|
|
|
|
secondValue() {
|
|
if (this.range) {
|
|
this.$emit('input', [this.minValue, this.maxValue]);
|
|
}
|
|
},
|
|
|
|
min() {
|
|
this.setValues();
|
|
},
|
|
|
|
max() {
|
|
this.setValues();
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
valueChanged() {
|
|
if (this.range) {
|
|
return ![this.minValue, this.maxValue]
|
|
.every((item, index) => item === this.oldValue[index]);
|
|
} else {
|
|
return this.value !== this.oldValue;
|
|
}
|
|
},
|
|
setValues() {
|
|
if (this.min > this.max) {
|
|
console.error('[Element Error][Slider]min should not be greater than max.');
|
|
return;
|
|
}
|
|
const val = this.value;
|
|
if (this.range && Array.isArray(val)) {
|
|
if (val[1] < this.min) {
|
|
this.$emit('input', [this.min, this.min]);
|
|
} else if (val[0] > this.max) {
|
|
this.$emit('input', [this.max, this.max]);
|
|
} else if (val[0] < this.min) {
|
|
this.$emit('input', [this.min, val[1]]);
|
|
} else if (val[1] > this.max) {
|
|
this.$emit('input', [val[0], this.max]);
|
|
} else {
|
|
this.firstValue = val[0];
|
|
this.secondValue = val[1];
|
|
if (this.valueChanged()) {
|
|
this.dispatch('ElFormItem', 'el.form.change', [this.minValue, this.maxValue]);
|
|
this.oldValue = val.slice();
|
|
}
|
|
}
|
|
} else if (!this.range && typeof val === 'number' && !isNaN(val)) {
|
|
if (val < this.min) {
|
|
this.$emit('input', this.min);
|
|
} else if (val > this.max) {
|
|
this.$emit('input', this.max);
|
|
} else {
|
|
this.firstValue = val;
|
|
if (this.valueChanged()) {
|
|
this.dispatch('ElFormItem', 'el.form.change', val);
|
|
this.oldValue = val;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
setPosition(percent) {
|
|
const targetValue = this.min + percent * (this.max - this.min) / 100;
|
|
if (!this.range) {
|
|
this.$refs.button1.setPosition(percent);
|
|
return;
|
|
}
|
|
let button;
|
|
if (Math.abs(this.minValue - targetValue) < Math.abs(this.maxValue - targetValue)) {
|
|
button = this.firstValue < this.secondValue ? 'button1' : 'button2';
|
|
} else {
|
|
button = this.firstValue > this.secondValue ? 'button1' : 'button2';
|
|
}
|
|
this.$refs[button].setPosition(percent);
|
|
},
|
|
|
|
onSliderClick(event) {
|
|
if (this.sliderDisabled || this.dragging) return;
|
|
this.resetSize();
|
|
if (this.vertical) {
|
|
const sliderOffsetBottom = this.$refs.slider.getBoundingClientRect().bottom;
|
|
this.setPosition((sliderOffsetBottom - event.clientY) / this.sliderSize * 100);
|
|
} else {
|
|
const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
|
|
this.setPosition((event.clientX - sliderOffsetLeft) / this.sliderSize * 100);
|
|
}
|
|
this.emitChange();
|
|
},
|
|
|
|
resetSize() {
|
|
if (this.$refs.slider) {
|
|
this.sliderSize = this.$refs.slider[`client${ this.vertical ? 'Height' : 'Width' }`];
|
|
}
|
|
},
|
|
|
|
emitChange() {
|
|
this.$nextTick(() => {
|
|
this.$emit('change', this.range ? [this.minValue, this.maxValue] : this.value);
|
|
});
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
stops() {
|
|
if (!this.showStops || this.min > this.max) return [];
|
|
if (this.step === 0) {
|
|
process.env.NODE_ENV !== 'production' &&
|
|
console.warn('[Element Warn][Slider]step should not be 0.');
|
|
return [];
|
|
}
|
|
const stopCount = (this.max - this.min) / this.step;
|
|
const stepWidth = 100 * this.step / (this.max - this.min);
|
|
const result = [];
|
|
for (let i = 1; i < stopCount; i++) {
|
|
result.push(i * stepWidth);
|
|
}
|
|
if (this.range) {
|
|
return result.filter(step => {
|
|
return step < 100 * (this.minValue - this.min) / (this.max - this.min) ||
|
|
step > 100 * (this.maxValue - this.min) / (this.max - this.min);
|
|
});
|
|
} else {
|
|
return result.filter(step => step > 100 * (this.firstValue - this.min) / (this.max - this.min));
|
|
}
|
|
},
|
|
|
|
minValue() {
|
|
return Math.min(this.firstValue, this.secondValue);
|
|
},
|
|
|
|
maxValue() {
|
|
return Math.max(this.firstValue, this.secondValue);
|
|
},
|
|
|
|
barSize() {
|
|
return this.range
|
|
? `${ 100 * (this.maxValue - this.minValue) / (this.max - this.min) }%`
|
|
: `${ 100 * (this.firstValue - this.min) / (this.max - this.min) }%`;
|
|
},
|
|
|
|
barStart() {
|
|
return this.range
|
|
? `${ 100 * (this.minValue - this.min) / (this.max - this.min) }%`
|
|
: '0%';
|
|
},
|
|
|
|
precision() {
|
|
let precisions = [this.min, this.max, this.step].map(item => {
|
|
let decimal = ('' + item).split('.')[1];
|
|
return decimal ? decimal.length : 0;
|
|
});
|
|
return Math.max.apply(null, precisions);
|
|
},
|
|
|
|
runwayStyle() {
|
|
return this.vertical ? { height: this.height } : {};
|
|
},
|
|
|
|
barStyle() {
|
|
return this.vertical
|
|
? {
|
|
height: this.barSize,
|
|
bottom: this.barStart
|
|
} : {
|
|
width: this.barSize,
|
|
left: this.barStart
|
|
};
|
|
},
|
|
|
|
sliderDisabled() {
|
|
return this.disabled || (this.elForm || {}).disabled;
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
let valuetext;
|
|
if (this.range) {
|
|
if (Array.isArray(this.value)) {
|
|
this.firstValue = Math.max(this.min, this.value[0]);
|
|
this.secondValue = Math.min(this.max, this.value[1]);
|
|
} else {
|
|
this.firstValue = this.min;
|
|
this.secondValue = this.max;
|
|
}
|
|
this.oldValue = [this.firstValue, this.secondValue];
|
|
valuetext = `${this.firstValue}-${this.secondValue}`;
|
|
} else {
|
|
if (typeof this.value !== 'number' || isNaN(this.value)) {
|
|
this.firstValue = this.min;
|
|
} else {
|
|
this.firstValue = Math.min(this.max, Math.max(this.min, this.value));
|
|
}
|
|
this.oldValue = this.firstValue;
|
|
valuetext = this.firstValue;
|
|
}
|
|
this.$el.setAttribute('aria-valuetext', valuetext);
|
|
|
|
// label screen reader
|
|
this.$el.setAttribute('aria-label', this.label ? this.label : `slider between ${this.min} and ${this.max}`);
|
|
|
|
this.resetSize();
|
|
window.addEventListener('resize', this.resetSize);
|
|
},
|
|
|
|
beforeDestroy() {
|
|
window.removeEventListener('resize', this.resetSize);
|
|
}
|
|
};
|
|
</script>
|