diff --git a/components/index.js b/components/index.js
index 654b206c8..89e1c44df 100644
--- a/components/index.js
+++ b/components/index.js
@@ -138,3 +138,5 @@ const { Column: TableColumn, ColumnGroup: TableColumnGroup } = Table
export { Table, TableColumn, TableColumnGroup }
export { default as version } from './version'
+
+export { default as Slider } from './slider'
diff --git a/components/slider/demo/basic.md b/components/slider/demo/basic.md
new file mode 100644
index 000000000..ef36d92d6
--- /dev/null
+++ b/components/slider/demo/basic.md
@@ -0,0 +1,38 @@
+
+#### 基本
+基本滑动条。当 `range` 为 `true` 时,渲染为双滑块。当 `disabled` 为 `true` 时,滑块处于不可用状态。
+
+
+
+#### Basic
+Basic slider. When `range` is `true`, display as dual thumb mode. When `disable` is `true`, the slider will not be interactable.
+
+
+```html
+
+
+
+
+
+```
diff --git a/components/slider/demo/event.md b/components/slider/demo/event.md
new file mode 100644
index 000000000..c49e2888b
--- /dev/null
+++ b/components/slider/demo/event.md
@@ -0,0 +1,42 @@
+
+#### 事件
+当 Slider 的值发生改变时,会触发 `onChange` 事件,并把改变后的值作为参数传入。在 `onmouseup` 时,会触发 `onAfterChange` 事件,并把当前值作为参数传入。
+
+
+
+#### Event
+The `onChange` callback function will fire when the user changes the slider's value.
+The `onAfterChange` callback function will fire when `onmouseup` fired.
+
+
+```html
+
+
+
+
+
+```
+
diff --git a/components/slider/demo/icon-slider.md b/components/slider/demo/icon-slider.md
new file mode 100644
index 000000000..722bc8db5
--- /dev/null
+++ b/components/slider/demo/icon-slider.md
@@ -0,0 +1,74 @@
+
+#### 带 icon 的滑块
+滑块左右可以设置图标来表达业务含义。
+
+
+
+#### Slider with icon
+You can add an icon beside the slider to make it meaningful.
+
+
+```html
+
+
+
+
+
+```
+
diff --git a/components/slider/demo/index.vue b/components/slider/demo/index.vue
new file mode 100644
index 000000000..a04488de3
--- /dev/null
+++ b/components/slider/demo/index.vue
@@ -0,0 +1,59 @@
+
diff --git a/components/slider/demo/input-number.md b/components/slider/demo/input-number.md
new file mode 100644
index 000000000..3a8e51c45
--- /dev/null
+++ b/components/slider/demo/input-number.md
@@ -0,0 +1,59 @@
+
+#### 带输入框的滑块
+和 [数字输入框](#/cn/components/input-number/) 组件保持同步。
+
+
+
+#### Slider with InputNumber
+Synchronize with [InptNumber](#/us/components/input-number/) component.
+
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
diff --git a/components/slider/demo/mark.md b/components/slider/demo/mark.md
new file mode 100644
index 000000000..a4863ac3b
--- /dev/null
+++ b/components/slider/demo/mark.md
@@ -0,0 +1,67 @@
+
+#### 带标签的滑块
+使用 `marks` 属性标注分段式滑块,使用 `value` / `defaultValue` 指定滑块位置。当 `included=false` 时,表明不同标记间为并列关系。当 `step=null` 时,Slider 的可选值仅有 `marks` 标出来的部分。
+
+
+
+#### Graduated slider
+Using `marks` property to mark a graduated slider, use `value` or `defaultValue` to specify the position of thumb.
+When `included` is false, means that different thumbs are coordinative.
+when `step` is null, users can only slide the thumbs onto marks.
+
+
+```html
+
+
+
included=true
+
+
+
+
included=false
+
+
+
marks & step
+
+
+
step=null
+
+
+
+
+
+```
+
+
diff --git a/components/slider/demo/tip-formatter.md b/components/slider/demo/tip-formatter.md
new file mode 100644
index 000000000..83f0e428f
--- /dev/null
+++ b/components/slider/demo/tip-formatter.md
@@ -0,0 +1,33 @@
+
+#### 自定义提示
+使用 `tipFormatter` 可以格式化 `Tooltip` 的内容,设置 `tipFormatter={null}`,则隐藏 `Tooltip`。
+
+
+
+#### Customerize tooltip
+Use `tipFormatter` to format content of `Toolip`. If `tipFormatter` is null, hide it.
+
+
+```html
+
+
+
+
+```
+
diff --git a/components/slider/demo/vertical.md b/components/slider/demo/vertical.md
new file mode 100644
index 000000000..a393e4b6a
--- /dev/null
+++ b/components/slider/demo/vertical.md
@@ -0,0 +1,55 @@
+
+#### 垂直
+垂直方向的 Slider。
+
+
+
+#### Vertical
+The vertical Slider.
+
+
+```html
+
+
+
+
+
+```
+
diff --git a/components/slider/index.en-US.md b/components/slider/index.en-US.md
new file mode 100644
index 000000000..090b3d649
--- /dev/null
+++ b/components/slider/index.en-US.md
@@ -0,0 +1,31 @@
+## 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 } } |
+| 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\[] | |
+| vertical | If true, the slider will be vertical. | Boolean | false |
+
+
+### events
+| Events Name | Description | Arguments |
+| --- | --- | --- |
+| afterChange | Fire when `mouseup` is fired. | Function(value) | NOOP |
+| change | Callback function that is fired when the user changes the slider's value. | Function(value) | NOOP |
+
+## Methods
+
+| Name | Description |
+| ---- | ----------- |
+| blur() | remove focus |
+| focus() | get focus |
diff --git a/components/slider/index.jsx b/components/slider/index.jsx
new file mode 100644
index 000000000..dc235a75d
--- /dev/null
+++ b/components/slider/index.jsx
@@ -0,0 +1,145 @@
+import PropTypes from '../_util/vue-types'
+import BaseMixin from '../_util/BaseMixin'
+import { getOptionProps } from '../_util/props-util'
+import VcSlider from '../vc-slider/src/Slider'
+import VcRange from '../vc-slider/src/Range'
+import VcHandle from '../vc-slider/src/Handle'
+import Tooltip from '../tooltip'
+
+// export interface SliderMarks {
+// [key]: React.ReactNode | {
+// style: React.CSSProperties,
+// label: React.ReactNode,
+// };
+// }
+// const SliderMarks = PropTypes.shape({
+// style: PropTypes.object,
+// label: PropTypes.any,
+// }).loose
+
+export const SliderProps = () => ({
+ prefixCls: PropTypes.string,
+ tooltipPrefixCls: PropTypes.string,
+ range: PropTypes.bool,
+ min: PropTypes.number,
+ max: PropTypes.number,
+ step: PropTypes.oneOfType([PropTypes.number, PropTypes.any]),
+ marks: PropTypes.object,
+ dots: PropTypes.bool,
+ value: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.arrayOf(PropTypes.number),
+ ]),
+ defaultValue: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.arrayOf(PropTypes.number),
+ ]),
+ included: PropTypes.bool,
+ disabled: PropTypes.bool,
+ vertical: PropTypes.bool,
+ tipFormatter: PropTypes.oneOfType([
+ PropTypes.func,
+ PropTypes.object,
+ ]),
+ id: PropTypes.string,
+})
+
+export default {
+ name: 'Slider',
+ model: {
+ prop: 'value',
+ event: 'change',
+ },
+ mixins: [BaseMixin],
+ props: {
+ ...SliderProps(),
+ prefixCls: PropTypes.string.def('ant-slider'),
+ tooltipPrefixCls: PropTypes.string.def('ant-tooltip'),
+ tipFormatter: PropTypes.oneOfType([
+ PropTypes.func,
+ PropTypes.object,
+ ]).def(value => value.toString()),
+ },
+ data () {
+ return {
+ visibles: {},
+ }
+ },
+ methods: {
+ toggleTooltipVisible (index, visible) {
+ this.setState(({ visibles }) => ({
+ visibles: {
+ ...visibles,
+ [index]: visible,
+ },
+ }))
+ },
+ handleWithTooltip (h, { value, dragging, index, refStr, ...restProps }) {
+ const { tooltipPrefixCls, tipFormatter } = this.$props
+ const { visibles } = this
+ const visible = tipFormatter ? (visibles[index] || dragging) : false
+
+ const tooltipProps = {
+ props: {
+ prefixCls: tooltipPrefixCls,
+ title: tipFormatter ? tipFormatter(value) : '',
+ visible,
+ placement: 'top',
+ transitionName: 'zoom-down',
+ },
+ key: index,
+ }
+ const handleProps = {
+ props: {
+ value,
+ ...restProps,
+ },
+ attrs: {
+ refStr,
+ },
+ on: {
+ mouseenter: () => this.toggleTooltipVisible(index, true),
+ mouseleave: () => this.toggleTooltipVisible(index, false),
+ },
+ }
+ return (
+
+
+
+ )
+ },
+ focus () {
+ this.$refs.sliderRef.focus()
+ },
+ blur () {
+ this.$refs.sliderRef.focus()
+ },
+ },
+ render () {
+ const { range, ...restProps } = getOptionProps(this)
+ if (range) {
+ const vcRangeProps = {
+ props: {
+ handle: this.handleWithTooltip,
+ ...restProps,
+ },
+ ref: 'sliderRef',
+ on: this.$listeners,
+ }
+ return
+ }
+ const vcSliderProps = {
+ props: {
+ handle: this.handleWithTooltip,
+ ...restProps,
+ },
+ ref: 'sliderRef',
+ on: this.$listeners,
+ }
+ return
+ },
+}
diff --git a/components/slider/index.zh-CN.md b/components/slider/index.zh-CN.md
new file mode 100644
index 000000000..b8360fdce
--- /dev/null
+++ b/components/slider/index.zh-CN.md
@@ -0,0 +1,30 @@
+## 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 } } |
+| 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 |
+
+### 事件
+| 事件名称 | 说明 | 回调参数 |
+| --- | --- | --- |
+| afterChange | 与 `mouseup` 触发时机一致,把当前值作为参数传入。 | Function(value) | NOOP |
+| change | 当 Slider 的值发生改变时,会触发 change 事件,并把改变后的值作为参数传入。 | Function(value) | NOOP |
+
+## 方法
+
+| 名称 | 描述 |
+| --- | --- |
+| blur() | 移除焦点 |
+| focus() | 获取焦点 |
diff --git a/components/slider/style/index.js b/components/slider/style/index.js
new file mode 100644
index 000000000..581a385b2
--- /dev/null
+++ b/components/slider/style/index.js
@@ -0,0 +1,5 @@
+import '../../style/index.less'
+import './index.less'
+
+// style dependencies
+import '../../tooltip/style'
diff --git a/components/slider/style/index.less b/components/slider/style/index.less
new file mode 100644
index 000000000..e7962cb3d
--- /dev/null
+++ b/components/slider/style/index.less
@@ -0,0 +1,188 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+
+@slider-prefix-cls: ~"@{ant-prefix}-slider";
+
+.@{slider-prefix-cls} {
+ .reset-component;
+ position: relative;
+ margin: @slider-margin;
+ padding: 4px 0;
+ height: 12px;
+ cursor: pointer;
+
+ .vertical();
+
+ &-with-marks {
+ margin-bottom: 28px;
+ }
+
+ &-rail {
+ position: absolute;
+ width: 100%;
+ height: 4px;
+ border-radius: 2px;
+ background-color: @slider-rail-background-color;
+ transition: background-color .3s;
+ }
+
+ &-track {
+ position: absolute;
+ height: 4px;
+ border-radius: @border-radius-base;
+ background-color: @slider-track-background-color;
+ transition: background-color 0.3s ease;
+ }
+
+ &-handle {
+ position: absolute;
+ margin-left: -7px;
+ margin-top: -5px;
+ width: 14px;
+ height: 14px;
+ cursor: pointer;
+ border-radius: 50%;
+ border: solid 2px @slider-handle-color;
+ background-color: @component-background;
+ transition: border-color .3s, transform .3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
+
+ &:focus {
+ border-color: @slider-handle-color-focus;
+ box-shadow: 0 0 0 5px @slider-handle-color-focus-shadow;
+ outline: none;
+ }
+
+ &.@{ant-prefix}-tooltip-open {
+ border-color: @slider-handle-color-tooltip-open;
+ }
+ }
+
+ &:hover {
+ .@{slider-prefix-cls}-rail {
+ background-color: @slider-rail-background-color-hover;
+ }
+ .@{slider-prefix-cls}-track {
+ background-color: @slider-track-background-color-hover;
+ }
+ .@{slider-prefix-cls}-handle:not(.@{ant-prefix}-tooltip-open) {
+ border-color: @slider-handle-color-hover;
+ }
+ }
+
+ &-mark {
+ position: absolute;
+ top: 14px;
+ left: 0;
+ width: 100%;
+ font-size: @font-size-base;
+ }
+
+ &-mark-text {
+ position: absolute;
+ display: inline-block;
+ vertical-align: middle;
+ text-align: center;
+ cursor: pointer;
+ color: @text-color-secondary;
+
+ &-active {
+ color: @text-color;
+ }
+ }
+
+ &-step {
+ position: absolute;
+ width: 100%;
+ height: 4px;
+ background: transparent;
+ }
+
+ &-dot {
+ position: absolute;
+ top: -2px;
+ margin-left: -4px;
+ width: 8px;
+ height: 8px;
+ border: 2px solid @slider-dot-border-color;
+ background-color: @component-background;
+ cursor: pointer;
+ border-radius: 50%;
+ vertical-align: middle;
+ &:first-child {
+ margin-left: -4px;
+ }
+ &:last-child {
+ margin-left: -4px;
+ }
+ &-active {
+ border-color: @slider-dot-border-color-active;
+ }
+ }
+
+ &-disabled {
+ cursor: not-allowed;
+
+ .@{slider-prefix-cls}-track {
+ background-color: @slider-disabled-color !important;
+ }
+
+ .@{slider-prefix-cls}-handle,
+ .@{slider-prefix-cls}-dot {
+ border-color: @slider-disabled-color !important;
+ background-color: @component-background;
+ cursor: not-allowed;
+ box-shadow: none;
+ }
+
+ .@{slider-prefix-cls}-mark-text,
+ .@{slider-prefix-cls}-dot {
+ cursor: not-allowed !important;
+ }
+ }
+}
+
+.vertical() {
+ &-vertical {
+ width: 12px;
+ height: 100%;
+ margin: 6px 10px;
+ padding: 0 4px;
+
+ .@{slider-prefix-cls}-rail {
+ height: 100%;
+ width: 4px;
+ }
+
+ .@{slider-prefix-cls}-track {
+ width: 4px;
+ }
+
+ .@{slider-prefix-cls}-handle {
+ margin-left: -5px;
+ margin-bottom: -7px;
+ }
+
+ .@{slider-prefix-cls}-mark {
+ top: 0;
+ left: 12px;
+ width: 18px;
+ height: 100%;
+ }
+
+ .@{slider-prefix-cls}-mark-text {
+ left: 4px;
+ white-space: nowrap;
+ }
+
+ .@{slider-prefix-cls}-step {
+ width: 4px;
+ height: 100%;
+ }
+
+ .@{slider-prefix-cls}-dot {
+ top: auto;
+ left: 2px;
+ margin-bottom: -4px;
+ }
+ }
+}
diff --git a/components/style.js b/components/style.js
index 032e7069f..c272e39ac 100644
--- a/components/style.js
+++ b/components/style.js
@@ -34,4 +34,5 @@ import './steps/style'
import './breadcrumb/style'
import './calendar/style'
import './date-picker/style'
+import './slider/style'
import './table/style'
diff --git a/components/vc-slider/src/common/Marks.jsx b/components/vc-slider/src/common/Marks.jsx
index 0adbcafdd..fae9c0ffe 100644
--- a/components/vc-slider/src/common/Marks.jsx
+++ b/components/vc-slider/src/common/Marks.jsx
@@ -1,4 +1,5 @@
import classNames from 'classnames'
+import { isValidElement } from '../../../_util/props-util'
const Marks = {
functional: true,
@@ -21,10 +22,8 @@ const Marks = {
const range = max - min
const elements = marksKeys.map(parseFloat).sort((a, b) => a - b).map(point => {
const markPoint = marks[point]
- // todo
- // const markPointIsObject = typeof markPoint === 'object' &&
- // !React.isValidElement(markPoint)
- const markPointIsObject = typeof markPoint === 'object'
+ const markPointIsObject = typeof markPoint === 'object' &&
+ !isValidElement(markPoint)
const markLabel = markPointIsObject ? markPoint.label : markPoint
if (!markLabel && markLabel !== 0) {
return null
diff --git a/components/vc-slider/src/common/createSlider.jsx b/components/vc-slider/src/common/createSlider.jsx
index bfd73d133..899da3afe 100644
--- a/components/vc-slider/src/common/createSlider.jsx
+++ b/components/vc-slider/src/common/createSlider.jsx
@@ -36,6 +36,10 @@ export default function createSlider (Component) {
return {
name: 'createSlider',
mixins: [Component],
+ model: {
+ prop: 'value',
+ event: 'change',
+ },
props: initDefaultProps(propTypes, {
...Component.defaultProps,
prefixCls: 'rc-slider',
@@ -68,10 +72,6 @@ export default function createSlider (Component) {
dotStyle: {},
activeDotStyle: {},
}),
- model: {
- prop: 'value',
- event: 'change',
- },
data () {
if (process.env.NODE_ENV !== 'production') {
const { step, max, min } = this
@@ -152,7 +152,6 @@ export default function createSlider (Component) {
}
},
onBlur (e) {
- console.dir(e)
this.onEnd(e)
this.$emit('blur', e)
},
diff --git a/examples/routes.js b/examples/routes.js
index 674f13d15..49ad2a263 100644
--- a/examples/routes.js
+++ b/examples/routes.js
@@ -3,7 +3,7 @@ const AsyncComp = () => {
const hashs = window.location.hash.split('/')
const d = hashs[hashs.length - 1]
return {
- component: import(`../components/table/demo/${d}`),
+ component: import(`../components/slider/demo/${d}`),
}
}
export default [