mirror of https://github.com/ElemeFE/element
Slider: add disabled/use tooltip (#589)
parent
588523e808
commit
e51d753ffd
|
@ -19,7 +19,7 @@
|
||||||
- 修复 Input Number min max 属性设置后点击加减出现的崩溃的bug
|
- 修复 Input Number min max 属性设置后点击加减出现的崩溃的bug
|
||||||
- 优化 TimePicker/DatePicker 输入日期行为
|
- 优化 TimePicker/DatePicker 输入日期行为
|
||||||
- 修复 DatePicker 输入禁用状态的日期却生效的问题 #484
|
- 修复 DatePicker 输入禁用状态的日期却生效的问题 #484
|
||||||
|
- 新增 Slider 的 disabled 属性
|
||||||
|
|
||||||
#### 非兼容性更新
|
#### 非兼容性更新
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
return {
|
return {
|
||||||
value1: 0,
|
value1: 0,
|
||||||
value2: 50,
|
value2: 50,
|
||||||
value3: 0,
|
value3: 42,
|
||||||
value4: 0,
|
value4: 0,
|
||||||
value5: 0
|
value5: 0,
|
||||||
|
value6: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +59,10 @@
|
||||||
<span class="demonstration">自定义初始值</span>
|
<span class="demonstration">自定义初始值</span>
|
||||||
<el-slider v-model="value2"></el-slider>
|
<el-slider v-model="value2"></el-slider>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">禁用</span>
|
||||||
|
<el-slider v-model="value3" disabled></el-slider>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -83,14 +88,14 @@
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<span class="demonstration">不显示间断点</span>
|
<span class="demonstration">不显示间断点</span>
|
||||||
<el-slider
|
<el-slider
|
||||||
v-model="value3"
|
v-model="value4"
|
||||||
:step="10">
|
:step="10">
|
||||||
</el-slider>
|
</el-slider>
|
||||||
</div>
|
</div>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<span class="demonstration">显示间断点</span>
|
<span class="demonstration">显示间断点</span>
|
||||||
<el-slider
|
<el-slider
|
||||||
v-model="value4"
|
v-model="value5"
|
||||||
:step="10"
|
:step="10"
|
||||||
show-stops>
|
show-stops>
|
||||||
</el-slider>
|
</el-slider>
|
||||||
|
@ -119,7 +124,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<el-slider
|
<el-slider
|
||||||
v-model="value5"
|
v-model="value6"
|
||||||
show-input>
|
show-input>
|
||||||
</el-slider>
|
</el-slider>
|
||||||
</div>
|
</div>
|
||||||
|
@ -142,6 +147,7 @@
|
||||||
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
||||||
| min | 最小值 | number | — | 0 |
|
| min | 最小值 | number | — | 0 |
|
||||||
| max | 最大值 | number | — | 100 |
|
| max | 最大值 | number | — | 100 |
|
||||||
|
| disabled | 是否禁用 | boolean | — | false |
|
||||||
| step | 步长 | number | — | 1 |
|
| step | 步长 | number | — | 1 |
|
||||||
| show-input | 是否显示输入框 | boolean | — | false |
|
| show-input | 是否显示输入框 | boolean | — | false |
|
||||||
| show-stops | 是否显示间断点 | boolean | — | false |
|
| show-stops | 是否显示间断点 | boolean | — | false |
|
||||||
|
|
|
@ -7,35 +7,37 @@
|
||||||
@keyup.native="onInputChange"
|
@keyup.native="onInputChange"
|
||||||
ref="input"
|
ref="input"
|
||||||
:step="step"
|
:step="step"
|
||||||
|
:disabled="disabled"
|
||||||
:min="min"
|
:min="min"
|
||||||
:max="max"
|
:max="max"
|
||||||
size="small">
|
size="small">
|
||||||
</el-input-number>
|
</el-input-number>
|
||||||
<div class="el-slider__runway"
|
<div class="el-slider__runway"
|
||||||
:class="{ 'show-input': showInput }"
|
:class="{ 'show-input': showInput, 'disabled': disabled }"
|
||||||
@click="onSliderClick" ref="slider">
|
@click="onSliderClick" ref="slider">
|
||||||
<div class="el-slider__bar" :style="{ width: currentPosition }"></div>
|
<div class="el-slider__bar" :style="{ width: currentPosition }"></div>
|
||||||
<div
|
<div
|
||||||
class="el-slider__button-wrapper"
|
class="el-slider__button-wrapper"
|
||||||
@mouseenter="hovering = true"
|
@mouseenter="handleMouseEnter"
|
||||||
@mouseleave="hovering = false"
|
@mouseleave="handleMouseLeave"
|
||||||
@mousedown="onButtonDown"
|
@mousedown="onButtonDown"
|
||||||
:style="{left: currentPosition}" ref="button">
|
:class="{ 'hover': hovering, 'dragging': dragging }"
|
||||||
|
:style="{left: currentPosition}"
|
||||||
|
ref="button">
|
||||||
|
<el-tooltip placement="top" ref="tooltip">
|
||||||
|
<span slot="content">{{ value }}</span>
|
||||||
<div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div>
|
<div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<transition name="popper-fade">
|
|
||||||
<div class="el-slider__pop" v-show="showTip" ref="pop">{{ value }}</div>
|
|
||||||
</transition>
|
|
||||||
<div class="el-slider__stop" v-for="item in stops" :style="{ 'left': item + '%' }" v-if="showStops"></div>
|
<div class="el-slider__stop" v-for="item in stops" :style="{ 'left': item + '%' }" v-if="showStops"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script type="text/babel">
|
<script type="text/babel">
|
||||||
import Popper from 'element-ui/src/utils/popper';
|
|
||||||
import ElInputNumber from 'element-ui/packages/input-number/index.js';
|
import ElInputNumber from 'element-ui/packages/input-number/index.js';
|
||||||
|
import ElTooltip from 'element-ui/packages/tooltip/index.js';
|
||||||
import { getStyle } from 'wind-dom/src/style';
|
import { getStyle } from 'wind-dom/src/style';
|
||||||
import { addClass, removeClass } from 'wind-dom/src/class';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ElSlider',
|
name: 'ElSlider',
|
||||||
|
@ -68,24 +70,27 @@
|
||||||
showStops: {
|
showStops: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ElInputNumber
|
ElInputNumber,
|
||||||
|
ElTooltip
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
inputValue: null,
|
inputValue: null,
|
||||||
timeout: null,
|
timeout: null,
|
||||||
showTip: false,
|
|
||||||
hovering: false,
|
hovering: false,
|
||||||
dragging: false,
|
dragging: false,
|
||||||
startX: 0,
|
startX: 0,
|
||||||
currentX: 0,
|
currentX: 0,
|
||||||
startPos: 0,
|
startPos: 0,
|
||||||
popper: null,
|
|
||||||
newPos: null,
|
newPos: null,
|
||||||
oldValue: this.value,
|
oldValue: this.value,
|
||||||
currentPosition: (this.value - this.min) / (this.max - this.min) * 100 + '%'
|
currentPosition: (this.value - this.min) / (this.max - this.min) * 100 + '%'
|
||||||
|
@ -97,21 +102,6 @@
|
||||||
this.$emit('input', Number(val));
|
this.$emit('input', Number(val));
|
||||||
},
|
},
|
||||||
|
|
||||||
showTip(val) {
|
|
||||||
if (val) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.updatePopper();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.timeout = setTimeout(() => {
|
|
||||||
if (this.popper) {
|
|
||||||
this.popper.destroy();
|
|
||||||
this.popper = null;
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
value(val) {
|
value(val) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.updatePopper();
|
this.updatePopper();
|
||||||
|
@ -130,27 +120,18 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
handlePopperStyle() {
|
handleMouseEnter() {
|
||||||
let placementMap = { top: 'bottom', bottom: 'top' };
|
this.hovering = true;
|
||||||
let placement = this.popper._popper.getAttribute('x-placement').split('-')[0];
|
this.$refs.tooltip.showPopper = true;
|
||||||
let origin = placementMap[placement];
|
},
|
||||||
addClass(this.popper._popper, placement);
|
|
||||||
removeClass(this.popper._popper, placementMap[placement]);
|
handleMouseLeave() {
|
||||||
this.popper._popper.style.transformOrigin = `center ${ origin }`;
|
this.hovering = false;
|
||||||
|
this.$refs.tooltip.showPopper = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
updatePopper() {
|
updatePopper() {
|
||||||
if (this.popper) {
|
this.$refs.tooltip.updatePopper();
|
||||||
clearTimeout(this.timeout);
|
|
||||||
this.popper.update();
|
|
||||||
this.handlePopperStyle();
|
|
||||||
} else {
|
|
||||||
this.popper = new Popper(this.$refs.button, this.$refs.pop, { gpuAcceleration: false, placement: 'top' });
|
|
||||||
this.popper.onCreate(() => {
|
|
||||||
this.handlePopperStyle();
|
|
||||||
});
|
|
||||||
this.updatePopper();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setPosition(newPos) {
|
setPosition(newPos) {
|
||||||
|
@ -169,6 +150,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
onSliderClick(event) {
|
onSliderClick(event) {
|
||||||
|
if (this.disabled) return;
|
||||||
var currentX = event.clientX;
|
var currentX = event.clientX;
|
||||||
var sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
|
var sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
|
||||||
var newPos = (currentX - sliderOffsetLeft) / this.$sliderWidth * 100;
|
var newPos = (currentX - sliderOffsetLeft) / this.$sliderWidth * 100;
|
||||||
|
@ -192,6 +174,7 @@
|
||||||
|
|
||||||
onDragging(event) {
|
onDragging(event) {
|
||||||
if (this.dragging) {
|
if (this.dragging) {
|
||||||
|
this.$refs.tooltip.showPopper = true;
|
||||||
this.currentX = event.clientX;
|
this.currentX = event.clientX;
|
||||||
var diff = (this.currentX - this.startX) / this.$sliderWidth * 100;
|
var diff = (this.currentX - this.startX) / this.$sliderWidth * 100;
|
||||||
this.newPos = this.startPos + diff;
|
this.newPos = this.startPos + diff;
|
||||||
|
@ -202,6 +185,7 @@
|
||||||
onDragEnd() {
|
onDragEnd() {
|
||||||
if (this.dragging) {
|
if (this.dragging) {
|
||||||
this.dragging = false;
|
this.dragging = false;
|
||||||
|
this.$refs.tooltip.showPopper = false;
|
||||||
this.setPosition(this.newPos);
|
this.setPosition(this.newPos);
|
||||||
window.removeEventListener('mousemove', this.onDragging);
|
window.removeEventListener('mousemove', this.onDragging);
|
||||||
window.removeEventListener('mouseup', this.onDragEnd);
|
window.removeEventListener('mouseup', this.onDragEnd);
|
||||||
|
@ -209,6 +193,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
onButtonDown(event) {
|
onButtonDown(event) {
|
||||||
|
if (this.disabled) return;
|
||||||
this.onDragStart(event);
|
this.onDragStart(event);
|
||||||
window.addEventListener('mousemove', this.onDragging);
|
window.addEventListener('mousemove', this.onDragging);
|
||||||
window.addEventListener('mouseup', this.onDragEnd);
|
window.addEventListener('mouseup', this.onDragEnd);
|
||||||
|
@ -220,10 +205,6 @@
|
||||||
return parseInt(getStyle(this.$refs.slider, 'width'), 10);
|
return parseInt(getStyle(this.$refs.slider, 'width'), 10);
|
||||||
},
|
},
|
||||||
|
|
||||||
showTip() {
|
|
||||||
return this.dragging || this.hovering;
|
|
||||||
},
|
|
||||||
|
|
||||||
stops() {
|
stops() {
|
||||||
let stopCount = (this.max - this.value) / this.step;
|
let stopCount = (this.max - this.value) / this.step;
|
||||||
let result = [];
|
let result = [];
|
||||||
|
@ -245,12 +226,6 @@
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.inputValue = this.inputValue || this.value;
|
this.inputValue = this.inputValue || this.value;
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
if (this.popper) {
|
|
||||||
this.popper.destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -377,8 +377,9 @@
|
||||||
--------------------------*/
|
--------------------------*/
|
||||||
--slider-main-background-color: var(--color-primary);
|
--slider-main-background-color: var(--color-primary);
|
||||||
--slider-runway-background-color: #e5e9f2;
|
--slider-runway-background-color: #e5e9f2;
|
||||||
--slider-runway-hover-color: #1d8ce0;
|
--slider-button-hover-color: #1d8ce0;
|
||||||
--slider-stop-background-color: #c0ccda;
|
--slider-stop-background-color: #c0ccda;
|
||||||
|
--slider-disable-color: #c0ccda;
|
||||||
|
|
||||||
/*Steps
|
/*Steps
|
||||||
--------------------------*/
|
--------------------------*/
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
@charset "UTF-8";
|
@charset "UTF-8";
|
||||||
@import "./input-number.css";
|
@import "./input-number.css";
|
||||||
|
@import "./tooltip.css";
|
||||||
@import "./common/var.css";
|
@import "./common/var.css";
|
||||||
|
|
||||||
@component-namespace el {
|
@component-namespace el {
|
||||||
|
@ -19,6 +20,42 @@
|
||||||
margin-right: 160px;
|
margin-right: 160px;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
.el-slider__bar, .el-slider__button {
|
||||||
|
background-color: var(--slider-disable-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-slider__button-wrapper {
|
||||||
|
&:hover,
|
||||||
|
&.hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-slider__button {
|
||||||
|
&:hover,
|
||||||
|
&.hover,
|
||||||
|
&.dragging {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@e input {
|
@e input {
|
||||||
|
@ -41,60 +78,42 @@
|
||||||
top: -16px;
|
top: -16px;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.el-tooltip {
|
||||||
|
margin-top: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.hover {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dragging {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@e button {
|
@e button {
|
||||||
size: 12px;
|
size: 12px;
|
||||||
background-color: var(--slider-main-background-color);
|
background-color: var(--slider-main-background-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
position: absolute;
|
|
||||||
top: 12px;
|
|
||||||
left: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: .2s;
|
transition: .2s;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&.hover,
|
&.hover,
|
||||||
&.dragging {
|
&.dragging {
|
||||||
transform: scale(1.5);
|
transform: scale(1.5);
|
||||||
background-color: #1d8ce0;
|
background-color: var(--slider-button-hover-color);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@e pop {
|
&:hover,
|
||||||
@utils-user-select none;
|
&.hover {
|
||||||
font-size: 12px;
|
cursor: grab;
|
||||||
line-height: 26px;
|
|
||||||
text-align: center;
|
|
||||||
size: 26px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: var(--slider-main-background-color);
|
|
||||||
color: #fff;
|
|
||||||
cursor: default;
|
|
||||||
z-index: var(--index-top);
|
|
||||||
transition: transform .3s, opacity .3s;
|
|
||||||
transform-origin: center bottom;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
triangle: 9px top var(--slider-main-background-color);
|
|
||||||
position: absolute;
|
|
||||||
top: -14px;
|
|
||||||
left: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&.dragging {
|
||||||
triangle: 9px bottom var(--slider-main-background-color);
|
cursor: grabbing;
|
||||||
position: absolute;
|
|
||||||
bottom: -14px;
|
|
||||||
left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.top::after {
|
|
||||||
content: '';
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom::before {
|
|
||||||
content: '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,11 +124,5 @@
|
||||||
background-color: var(--slider-stop-background-color);
|
background-color: var(--slider-stop-background-color);
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.popper-fade-enter,
|
|
||||||
.popper-fade-leave-active {
|
|
||||||
transform: scale(0.1);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,7 @@ import Slider from 'packages/slider';
|
||||||
describe('Slider', () => {
|
describe('Slider', () => {
|
||||||
it('create', () => {
|
it('create', () => {
|
||||||
const vm = createTest(Slider);
|
const vm = createTest(Slider);
|
||||||
const popup = vm.$el.querySelector('.el-slider__pop');
|
expect(vm.value).to.equal(0);
|
||||||
expect(popup.textContent).to.equal('0');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not exceed min and max', done => {
|
it('should not exceed min and max', done => {
|
||||||
|
@ -36,7 +35,7 @@ describe('Slider', () => {
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show tooltip', done => {
|
it('show tooltip', () => {
|
||||||
const vm = createVue({
|
const vm = createVue({
|
||||||
template: `
|
template: `
|
||||||
<div>
|
<div>
|
||||||
|
@ -52,14 +51,10 @@ describe('Slider', () => {
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
const slider = vm.$children[0];
|
const slider = vm.$children[0];
|
||||||
const popup = vm.$el.querySelector('.el-slider__pop');
|
slider.handleMouseEnter();
|
||||||
slider.onDragStart({ clientX: 0 });
|
expect(slider.$refs.tooltip.showPopper).to.true;
|
||||||
vm.$nextTick(() => {
|
slider.handleMouseLeave();
|
||||||
expect(popup.style.display).to.not.equal('none');
|
expect(slider.$refs.tooltip.showPopper).to.false;
|
||||||
slider.onDragEnd();
|
|
||||||
expect(slider.showTip).to.false;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('drag', done => {
|
it('drag', done => {
|
||||||
|
@ -110,6 +105,31 @@ describe('Slider', () => {
|
||||||
}, 150);
|
}, 150);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('disabled', done => {
|
||||||
|
const vm = createVue({
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<el-slider v-model="value" disabled></el-slider>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
const slider = vm.$children[0];
|
||||||
|
setTimeout(() => {
|
||||||
|
slider.onButtonDown({ clientX: 0 });
|
||||||
|
slider.onDragging({ clientX: 100 });
|
||||||
|
slider.onDragEnd();
|
||||||
|
slider.onSliderClick({ clientX: 200 });
|
||||||
|
expect(vm.value).to.equal(0);
|
||||||
|
done();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
it('show input', done => {
|
it('show input', done => {
|
||||||
const vm = createVue({
|
const vm = createVue({
|
||||||
template: `
|
template: `
|
||||||
|
|
Loading…
Reference in New Issue