+
-
diff --git a/components/slider/index.en-US.md b/components/slider/index.en-US.md
index a9e30e340..65a104f78 100644
--- a/components/slider/index.en-US.md
+++ b/components/slider/index.en-US.md
@@ -1,21 +1,24 @@
## API
-| Property | Description | Type | Default |
-| --- | --- | --- | --- |
-| autoFocus | get focus when component mounted | boolean | false |
-| defaultValue | The default value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | 0 or \[0, 0] |
-| disabled | If true, the slider will not be interactable. | boolean | false |
-| dots | Whether the thumb can drag over tick only. | boolean | false |
-| included | Make effect when `marks` not null,`true` means containment and `false` means coordinative | boolean | true |
-| marks | Tick mark of Slider, type of key must be `number`, and must in closed interval \[min, max], each mark can declare its own style. | object | { number: string\|VNode } or { number: { style: object, label: string\|VNode } } or { number: () => VNode } |
-| max | The maximum value the slider can slide to | number | 100 |
-| min | The minimum value the slider can slide to. | number | 0 |
-| range | dual thumb mode | boolean | false |
-| step | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min) . When `marks` no null, `step` can be `null`. | number\|null | 1 |
-| tipFormatter | Slider will pass its value to `tipFormatter`, and display its value in Tooltip, and hide Tooltip when return value is null. | Function\|null | IDENTITY |
-| value(v-model) | The value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | |
+| Property | Description | Type | Default | Version |
+| --- | --- | --- | --- | --- |
+| autoFocus | get focus when component mounted | boolean | false | |
+| defaultValue | The default value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | 0 or \[0, 0] | |
+| disabled | If true, the slider will not be interactable. | boolean | false | |
+| dots | Whether the thumb can drag over tick only. | boolean | false | |
+| included | Make effect when `marks` not null,`true` means containment and `false` means coordinative | boolean | true | |
+| marks | Tick mark of Slider, type of key must be `number`, and must in closed interval \[min, max], each mark can declare its own style. | object | { number: string\|VNode } or { number: { style: object, label: string\|VNode } } or { number: () => VNode } | |
+| max | The maximum value the slider can slide to | number | 100 | |
+| min | The minimum value the slider can slide to. | number | 0 | |
+| range | dual thumb mode | boolean | false | |
+| reverse | reverse the component | boolean | false | 1.5.0 |
+| step | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min) . When `marks` no null, `step` can be `null`. | number\|null | 1 | |
+| tipFormatter | Slider will pass its value to `tipFormatter`, and display its value in Tooltip, and hide Tooltip when return value is null. | Function\|null | IDENTITY | |
+| value(v-model) | The value of slider. When `range` is `false`, use `number`, otherwise, use `[number, number]` | number\|number\[] | | |
| vertical | If true, the slider will be vertical. | Boolean | false |
-| tooltipVisible | If true, Tooltip will show always, or it will not show anyway, even if dragging or hovering. | Boolean | |
+| tooltipPlacement | Set Tooltip display position. Ref [`Tooltip`](/components/tooltip/). | string | | 1.5.0 |
+| tooltipVisible | If true, Tooltip will show always, or it will not show anyway, even if dragging or hovering. | Boolean | | |
+| getTooltipPopupContainer | The DOM container of the Tooltip, the default behavior is to create a div element in body. | Function | () => document.body | 1.5.0 |
### events
diff --git a/components/slider/index.jsx b/components/slider/index.jsx
index 63acf568b..734debb74 100644
--- a/components/slider/index.jsx
+++ b/components/slider/index.jsx
@@ -7,6 +7,7 @@ import VcHandle from '../vc-slider/src/Handle';
import Tooltip from '../tooltip';
import Base from '../base';
import { ConfigConsumerProps } from '../config-provider';
+import abstractTooltipProps from '../tooltip/abstractTooltipProps';
// export interface SliderMarks {
// [key]: React.ReactNode | {
@@ -18,11 +19,12 @@ import { ConfigConsumerProps } from '../config-provider';
// style: PropTypes.object,
// label: PropTypes.any,
// }).loose
-
+const tooltipProps = abstractTooltipProps();
export const SliderProps = () => ({
prefixCls: PropTypes.string,
tooltipPrefixCls: PropTypes.string,
range: PropTypes.bool,
+ reverse: PropTypes.bool,
min: PropTypes.number,
max: PropTypes.number,
step: PropTypes.oneOfType([PropTypes.number, PropTypes.any]),
@@ -35,6 +37,8 @@ export const SliderProps = () => ({
vertical: PropTypes.bool,
tipFormatter: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
tooltipVisible: PropTypes.bool,
+ tooltipPlacement: tooltipProps.placement,
+ getTooltipPopupContainer: PropTypes.func,
});
const Slider = {
@@ -67,8 +71,17 @@ const Slider = {
},
}));
},
- handleWithTooltip(tooltipPrefixCls, { value, dragging, index, directives, on, ...restProps }) {
- const { tipFormatter, tooltipVisible } = this.$props;
+ handleWithTooltip(
+ tooltipPrefixCls,
+ prefixCls,
+ { value, dragging, index, directives, on, ...restProps },
+ ) {
+ const {
+ tipFormatter,
+ tooltipVisible,
+ tooltipPlacement,
+ getTooltipPopupContainer,
+ } = this.$props;
const { visibles } = this;
const isTipFormatter = tipFormatter ? visibles[index] || dragging : false;
const visible = tooltipVisible || (tooltipVisible === undefined && isTipFormatter);
@@ -77,8 +90,10 @@ const Slider = {
prefixCls: tooltipPrefixCls,
title: tipFormatter ? tipFormatter(value) : '',
visible,
- placement: 'top',
- transitionName: 'fade',
+ placement: tooltipPlacement || 'top',
+ transitionName: 'zoom-down',
+ overlayClassName: `${prefixCls}-tooltip`,
+ getPopupContainer: getTooltipPopupContainer || (() => document.body),
},
key: index,
};
@@ -124,7 +139,7 @@ const Slider = {
...restProps,
prefixCls,
tooltipPrefixCls,
- handle: info => this.handleWithTooltip(tooltipPrefixCls, info),
+ handle: info => this.handleWithTooltip(tooltipPrefixCls, prefixCls, info),
},
ref: 'sliderRef',
on: listeners,
@@ -136,7 +151,7 @@ const Slider = {
...restProps,
prefixCls,
tooltipPrefixCls,
- handle: info => this.handleWithTooltip(tooltipPrefixCls, info),
+ handle: info => this.handleWithTooltip(tooltipPrefixCls, prefixCls, info),
},
ref: 'sliderRef',
on: listeners,
diff --git a/components/slider/index.zh-CN.md b/components/slider/index.zh-CN.md
index 908be107d..887a63d71 100644
--- a/components/slider/index.zh-CN.md
+++ b/components/slider/index.zh-CN.md
@@ -1,21 +1,24 @@
## API
-| 参数 | 说明 | 类型 | 默认值 |
-| --- | --- | --- | --- |
-| autoFocus | 自动获取焦点 | boolean | false |
-| defaultValue | 设置初始取值。当 `range` 为 `false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | 0 or \[0, 0] |
-| disabled | 值为 `true` 时,滑块为禁用状态 | boolean | false |
-| dots | 是否只能拖拽到刻度上 | boolean | false |
-| included | `marks` 不为空对象时有效,值为 true 时表示值为包含关系,false 表示并列 | boolean | true |
-| marks | 刻度标记,key 的类型必须为 `number` 且取值在闭区间 \[min, max] 内,每个标签可以单独设置样式 | object | { number: string\|VNode } or { number: { style: object, label: string\|VNode } } or { number: () => VNode } |
-| max | 最大值 | number | 100 |
-| min | 最小值 | number | 0 |
-| range | 双滑块模式 | boolean | false |
-| step | 步长,取值必须大于 0,并且可被 (max - min) 整除。当 `marks` 不为空对象时,可以设置 `step` 为 `null`,此时 Slider 的可选值仅有 marks 标出来的部分。 | number\|null | 1 |
-| tipFormatter | Slider 会把当前值传给 `tipFormatter`,并在 Tooltip 中显示 `tipFormatter` 的返回值,若为 null,则隐藏 Tooltip。 | Function\|null | IDENTITY |
-| value(v-model) | 设置当前取值。当 `range` 为 `false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | |
-| vertical | 值为 `true` 时,Slider 为垂直方向 | Boolean | false |
-| tooltipVisible | 值为`true`时,Tooltip 将会始终显示;否则始终不显示,哪怕在拖拽及移入时。 | Boolean | |
+| 参数 | 说明 | 类型 | 默认值 | 版本 |
+| --- | --- | --- | --- | --- |
+| autoFocus | 自动获取焦点 | boolean | false | |
+| defaultValue | 设置初始取值。当 `range` 为 `false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | 0 or \[0, 0] | |
+| disabled | 值为 `true` 时,滑块为禁用状态 | boolean | false | |
+| dots | 是否只能拖拽到刻度上 | boolean | false | |
+| included | `marks` 不为空对象时有效,值为 true 时表示值为包含关系,false 表示并列 | boolean | true | |
+| marks | 刻度标记,key 的类型必须为 `number` 且取值在闭区间 \[min, max] 内,每个标签可以单独设置样式 | object | { number: string\|VNode } or { number: { style: object, label: string\|VNode } } or { number: () => VNode } | |
+| max | 最大值 | number | 100 | |
+| min | 最小值 | number | 0 | |
+| range | 双滑块模式 | boolean | false | |
+| reverse | 反向坐标轴 | boolean | false | 1.5.0 |
+| step | 步长,取值必须大于 0,并且可被 (max - min) 整除。当 `marks` 不为空对象时,可以设置 `step` 为 `null`,此时 Slider 的可选值仅有 marks 标出来的部分。 | number\|null | 1 | |
+| tipFormatter | Slider 会把当前值传给 `tipFormatter`,并在 Tooltip 中显示 `tipFormatter` 的返回值,若为 null,则隐藏 Tooltip。 | Function\|null | IDENTITY | |
+| value(v-model) | 设置当前取值。当 `range` 为 `false` 时,使用 `number`,否则用 `[number, number]` | number\|number\[] | | |
+| vertical | 值为 `true` 时,Slider 为垂直方向 | Boolean | false | |
+| tooltipPlacement | 设置 Tooltip 展示位置。参考 [`Tooltip`](/components/tooltip/)。 | string | | 1.5.0 |
+| tooltipVisible | 值为`true`时,Tooltip 将会始终显示;否则始终不显示,哪怕在拖拽及移入时。 | Boolean | | |
+| getTooltipPopupContainer | Tooltip 渲染父节点,默认渲染到 body 上。 | Function | () => document.body | 1.5.0 |
### 事件
diff --git a/components/vc-slider/index.js b/components/vc-slider/index.js
index f3d986e67..89dcb1e60 100644
--- a/components/vc-slider/index.js
+++ b/components/vc-slider/index.js
@@ -1,4 +1,4 @@
-// base rc-slider 8.6.6
+// base rc-slider 8.7.1
import Vue from 'vue';
import ref from 'vue-ref';
import Slider from './src/';
diff --git a/components/vc-slider/src/Handle.jsx b/components/vc-slider/src/Handle.jsx
index 0394cb80a..469facf73 100644
--- a/components/vc-slider/src/Handle.jsx
+++ b/components/vc-slider/src/Handle.jsx
@@ -17,6 +17,7 @@ export default {
value: PropTypes.number,
tabIndex: PropTypes.number,
className: PropTypes.string,
+ reverse: PropTypes.bool,
// handleFocus: PropTypes.func.def(noop),
// handleBlur: PropTypes.func.def(noop),
},
@@ -68,14 +69,32 @@ export default {
},
},
render() {
- const { prefixCls, vertical, offset, disabled, min, max, value, tabIndex } = getOptionProps(
- this,
- );
+ const {
+ prefixCls,
+ vertical,
+ reverse,
+ offset,
+ disabled,
+ min,
+ max,
+ value,
+ tabIndex,
+ } = getOptionProps(this);
const className = classNames(this.$props.className, {
[`${prefixCls}-handle-click-focused`]: this.clickFocused,
});
- const postionStyle = vertical ? { bottom: `${offset}%` } : { left: `${offset}%` };
+ const positionStyle = vertical
+ ? {
+ [reverse ? 'top' : 'bottom']: `${offset}%`,
+ [reverse ? 'bottom' : 'top']: 'auto',
+ transform: `translateY(+50%)`,
+ }
+ : {
+ [reverse ? 'right' : 'left']: `${offset}%`,
+ [reverse ? 'left' : 'right']: 'auto',
+ transform: `translateX(${reverse ? '+' : '-'}50%)`,
+ };
const ariaProps = {
'aria-valuemin': min,
@@ -83,11 +102,15 @@ export default {
'aria-valuenow': value,
'aria-disabled': !!disabled,
};
+ let _tabIndex = tabIndex || 0;
+ if (disabled || tabIndex === null) {
+ _tabIndex = null;
+ }
const handleProps = {
attrs: {
role: 'slider',
- tabIndex: disabled ? null : tabIndex || 0,
+ tabIndex: _tabIndex,
...ariaProps,
},
class: className,
@@ -98,7 +121,7 @@ export default {
mousedown: this.handleMousedown,
},
ref: 'handle',
- style: postionStyle,
+ style: positionStyle,
};
return
;
},
diff --git a/components/vc-slider/src/Range.jsx b/components/vc-slider/src/Range.jsx
index 551dad5ed..151167397 100644
--- a/components/vc-slider/src/Range.jsx
+++ b/components/vc-slider/src/Range.jsx
@@ -6,6 +6,22 @@ import Track from './common/Track';
import createSlider from './common/createSlider';
import * as utils from './utils';
+const trimAlignValue = ({ value, handle, bounds, props }) => {
+ const { allowCross, pushable } = props;
+ const thershold = Number(pushable);
+ const valInRange = utils.ensureValueInRange(value, props);
+ let valNotConflict = valInRange;
+ if (!allowCross && handle != null && bounds !== undefined) {
+ if (handle > 0 && valInRange <= bounds[handle - 1] + thershold) {
+ valNotConflict = bounds[handle - 1] + thershold;
+ }
+ if (handle < bounds.length - 1 && valInRange >= bounds[handle + 1] - thershold) {
+ valNotConflict = bounds[handle + 1] - thershold;
+ }
+ }
+ return utils.ensureValuePrecision(valNotConflict, props);
+};
+
const rangeProps = {
defaultValue: PropTypes.arrayOf(PropTypes.number),
value: PropTypes.arrayOf(PropTypes.number),
@@ -13,10 +29,12 @@ const rangeProps = {
pushable: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
allowCross: PropTypes.bool,
disabled: PropTypes.bool,
+ reverse: PropTypes.bool,
tabIndex: PropTypes.arrayOf(PropTypes.number),
prefixCls: PropTypes.string,
min: PropTypes.number,
max: PropTypes.number,
+ autoFocus: PropTypes.bool,
};
const Range = {
name: 'Range',
@@ -36,7 +54,13 @@ const Range = {
if (value === undefined) {
value = defaultValue;
}
- const bounds = value.map((v, i) => this.trimAlignValue(v, i));
+ const bounds = value.map((v, i) =>
+ trimAlignValue({
+ value: v,
+ handle: i,
+ props: this.$props,
+ }),
+ );
const recent = bounds[0] === max ? 0 : bounds.length - 1;
return {
sHandle: null,
@@ -47,37 +71,43 @@ const Range = {
watch: {
value: {
handler(val) {
- const { min, max } = this;
- this.setChangeValue(val, min, max);
+ const { min, max, bounds } = this;
+ this.setChangeValue(val || bounds, min, max);
},
deep: true,
},
min(val) {
- const { bounds, max } = this;
- this.setChangeValue(bounds, val, max);
+ const { value = bounds, max } = this;
+ this.setChangeValue(value, val, max);
},
max(val) {
- const { bounds, min } = this;
- this.setChangeValue(bounds, min, val);
+ const { value = bounds, min } = this;
+ this.setChangeValue(value, min, val);
},
},
methods: {
setChangeValue(value, min, max) {
const { bounds } = this;
- const newValue = value || bounds;
const minAmaxProps = {
min,
max,
};
- const nextBounds = newValue.map((v, i) => this.trimAlignValue(v, i, minAmaxProps));
+ const nextBounds = value.map((v, i) =>
+ trimAlignValue({
+ value: v,
+ handle: i,
+ bounds,
+ props: this.$props,
+ }),
+ );
if (nextBounds.length === bounds.length && nextBounds.every((v, i) => v === bounds[i]))
return;
this.setState({ bounds: nextBounds });
- if (value.some(v => utils.isValueOutOfRange(v, minAmaxProps))) {
+ if (value.some(v => utils.isValueOutOfRange(v, this.$props))) {
const newValues = value.map(v => {
- return utils.ensureValueInRange(v, minAmaxProps);
+ return utils.ensureValueInRange(v, this.$props);
});
this.$emit('change', newValues);
}
@@ -86,8 +116,18 @@ const Range = {
const isNotControlled = !hasProp(this, 'value');
if (isNotControlled) {
this.setState(state);
- } else if (state.sHandle !== undefined) {
- this.setState({ sHandle: state.sHandle });
+ } else {
+ const controlledState = {};
+
+ ['sHandle', 'recent'].forEach(item => {
+ if (state[item] !== undefined) {
+ controlledState[item] = state[item];
+ }
+ });
+
+ if (Object.keys(controlledState).length) {
+ this.setState(controlledState);
+ }
}
const data = { ...this.$data, ...state };
@@ -119,7 +159,7 @@ const Range = {
onEnd(force) {
const { sHandle } = this;
this.removeDocumentEvents();
- if (sHandle || force) {
+ if (sHandle !== null || force) {
this.$emit('afterChange', this.bounds);
}
this.setState({ sHandle: null });
@@ -134,14 +174,20 @@ const Range = {
this.moveTo(value);
},
onKeyboard(e) {
- const valueMutator = utils.getKeyboardValueMutator(e);
+ const { reverse, vertical } = this.$props;
+ const valueMutator = utils.getKeyboardValueMutator(e, vertical, reverse);
if (valueMutator) {
utils.pauseEvent(e);
const { bounds, sHandle } = this;
const oldValue = bounds[sHandle === null ? this.recent : sHandle];
const mutatedValue = valueMutator(oldValue, this.$props);
- const value = this.trimAlignValue(mutatedValue);
+ const value = trimAlignValue({
+ value: mutatedValue,
+ handle: sHandle,
+ bounds: bounds,
+ props: this.$props,
+ });
if (value === oldValue) return;
const isFromKeyboardEvent = true;
this.moveTo(value, isFromKeyboardEvent);
@@ -215,6 +261,7 @@ const Range = {
nextHandle = nextBounds.indexOf(value);
}
this.onChange({
+ recent: nextHandle,
sHandle: nextHandle,
bounds: nextBounds,
});
@@ -223,9 +270,11 @@ const Range = {
// so trigger focus will invoke handler's onEnd and another handler's onStart too early,
// cause onBeforeChange and onAfterChange receive wrong value.
// here use setState callback to hack,but not elegant
+ this.$emit('afterChange', nextBounds);
this.setState({}, () => {
this.handlesRefs[nextHandle].focus();
});
+ this.onEnd();
}
},
@@ -288,11 +337,14 @@ const Range = {
bounds[handle] = nextValue;
return true;
},
- trimAlignValue(v, handle, nextProps = {}) {
- const mergedProps = { ...this.$props, ...nextProps };
- const valInRange = utils.ensureValueInRange(v, mergedProps);
- const valNotConflict = this.ensureValueNotConflict(handle, valInRange, mergedProps);
- return utils.ensureValuePrecision(valNotConflict, mergedProps);
+ trimAlignValue(value) {
+ const { sHandle, bounds } = this;
+ return trimAlignValue({
+ value,
+ handle: sHandle,
+ bounds,
+ props: this.$props,
+ });
},
ensureValueNotConflict(handle, val, { allowCross, pushable: thershold }) {
const state = this.$data || {};
@@ -311,7 +363,7 @@ const Range = {
/* eslint-enable eqeqeq */
return val;
},
- getTrack({ bounds, prefixCls, vertical, included, offsets, trackStyle }) {
+ getTrack({ bounds, prefixCls, reverse, vertical, included, offsets, trackStyle }) {
return bounds.slice(0, -1).map((_, index) => {
const i = index + 1;
const trackClassName = classNames({
@@ -322,6 +374,7 @@ const Range = {