add slider v0.0.1

This commit is contained in:
wangxueliang 2018-04-01 19:23:56 +08:00
parent a3c1d235a1
commit cdd3cda787
18 changed files with 837 additions and 10 deletions

View File

@ -133,3 +133,5 @@ const { MonthPicker, RangePicker, WeekPicker } = DatePicker
export { DatePicker, MonthPicker, RangePicker, WeekPicker }
export { default as version } from './version'
export { default as Slider } from './slider'

View File

@ -0,0 +1,38 @@
<cn>
#### 基本
基本滑动条。当 `range``true` 时,渲染为双滑块。当 `disabled``true` 时,滑块处于不可用状态。
</cn>
<us>
#### Basic
Basic slider. When `range` is `true`, display as dual thumb mode. When `disable` is `true`, the slider will not be interactable.
</us>
```html
<template>
<div>
<a-slider :defaultValue="30" :disabled="disabled" />
<a-slider range :defaultValue="[20, 50]" :disabled="disabled" />
Disabled: <a-switch size="small" :checked="disabled" @change="handleDisabledChange" />
</div>
</template>
<script>
export default {
data() {
return {
disabled: false,
}
},
methods: {
handleDisabledChange(disabled) {
this.disabled = disabled
}
},
}
</script>
<style scoped>
.code-box-demo .ant-slider {
margin-bottom: 16px;
}
</style>
```

View File

@ -0,0 +1,42 @@
<cn>
#### 事件
当 Slider 的值发生改变时,会触发 `onChange` 事件,并把改变后的值作为参数传入。在 `onmouseup` 时,会触发 `onAfterChange` 事件,并把当前值作为参数传入。
</cn>
<us>
#### Event
The `onChange` callback function will fire when the user changes the slider's value.
The `onAfterChange` callback function will fire when `onmouseup` fired.
</us>
```html
<template>
<div class="code-box-demo">
<a-slider :defaultValue="30" @change="onChange" @afterChange="onAfterChange" />
<a-slider range :step="10" :defaultValue="[20, 50]" @change="onChange" @afterChange="onAfterChange" />
</div>
</template>
<script>
export default {
data() {
return {
disabled: false,
}
},
methods: {
onChange(value) {
console.log('change: ', value);
},
onAfterChange(value) {
console.log('afterChange: ', value);
}
},
}
</script>
<style scoped>
.code-box-demo .ant-slider {
margin-bottom: 16px;
}
</style>
```

View File

@ -0,0 +1,74 @@
<cn>
#### 带 icon 的滑块
滑块左右可以设置图标来表达业务含义。
</cn>
<us>
#### Slider with icon
You can add an icon beside the slider to make it meaningful.
</us>
```html
<template>
<div class="icon-wrapper">
<a-icon :style="{color: preColor}" type="frown-o" />
<a-slider :min="0" :max="20" @change="handleChange" :value="value" />
<a-slider :min="0" :max="20" v-model="value" />
<a-icon :style="{color: nextColor}" type="smile-o" />
</div>
</template>
<script>
export default {
data() {
return {
value: 0,
min: 0,
max: 20,
}
},
methods: {
handleChange(value) {
this.value = value
}
},
computed: {
preColor() {
const { max, min, value } = this
const mid = ((max - min) / 2).toFixed(5);
return value >= mid ? '' : 'rgba(0, 0, 0, .45)';
const nextColor = value >= mid ? 'rgba(0, 0, 0, .45)' : '';
},
nextColor() {
const { max, min, value } = this
const mid = ((max - min) / 2).toFixed(5);
return value >= mid ? 'rgba(0, 0, 0, .45)' : '';
}
}
}
</script>
<style scoped>
.icon-wrapper {
position: relative;
padding: 0px 30px;
}
.icon-wrapper .anticon {
position: absolute;
top: -2px;
width: 16px;
height: 16px;
line-height: 1;
font-size: 16px;
color: rgba(0, 0, 0, .25);
}
.icon-wrapper .anticon:first-child {
left: 0;
}
.icon-wrapper .anticon:last-child {
right: 0;
}
</style>
```

View File

@ -0,0 +1,59 @@
<script>
import Basic from './basic.md'
import InputNumber from './input-number.md'
import IconSlider from './icon-slider.md'
import TipFormatter from './tip-formatter.md'
import Event from './event.md'
import Mark from './mark.md'
import Vertical from './vertical.md'
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
const md = {
cn: `# Slider
滑动型输入器展示当前值和可选范围
## 何时使用
当用户需要在数值区间/自定义区间内进行选择时可为连续或离散值
## 代码演示`,
us: `# Slider
A Slider component for displaying current value and intervals in range.
# When To Use
To input a value in a range.
## Examples
`,
}
export default {
category: 'Components',
subtitle: '滑动输入条',
type: 'Data Entry',
title: 'Slider',
render () {
return (
<div>
<md cn={md.cn} us={md.us}/>
<br/>
<Basic />
<br />
<InputNumber />
<br />
<IconSlider />
<br />
<TipFormatter />
<br />
<Event />
<br />
<Mark />
<br />
<Vertical />
<br />
<api>
<template slot='cn'>
<CN/>
</template>
<US/>
</api>
</div>
)
},
}
</script>

View File

@ -0,0 +1,59 @@
<cn>
#### 带输入框的滑块
和 [数字输入框](#/cn/components/input-number/) 组件保持同步。
</cn>
<us>
#### Slider with InputNumber
Synchronize with [InptNumber](#/us/components/input-number/) component.
</us>
```html
<template>
<div>
<a-row>
<a-col :span="12">
<a-slider :min="1" :max="20" v-model="inputValue1" />
</a-col>
<!-- <a-col :span="4">
<a-inputNumber
:min="1"
:max="20"
style="marginLeft: 16px"
v-model="inputValue1"
/>
</a-ol> -->
</a-row>
<a-row>
<a-col :span="12">
<a-slider :min="0" :max="1" v-model="inputValue" :step="0.01" />
</a-col>
<!-- <a-col :span="4">
<a-inputNumber
:min="0"
:max="1"
:step="0.01"
style="marginLeft: 16px"
v-model="inputValue"
/>
</a-col> -->
</a-row>
</div>
</template>
<script>
export default {
data() {
return {
inputValue: 0,
inputValue1: 1,
}
},
}
</script>
<style scoped>
.code-box-demo .ant-slider {
margin-bottom: 16px;
}
</style>
```

View File

@ -0,0 +1,67 @@
<cn>
#### 带标签的滑块
使用 `marks` 属性标注分段式滑块,使用 `value` / `defaultValue` 指定滑块位置。当 `included=false` 时,表明不同标记间为并列关系。当 `step=null`Slider 的可选值仅有 `marks` 标出来的部分。
</cn>
<us>
#### 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.
</us>
```html
<template>
<div id="components-slider-demo-mark">
<h4>included=true</h4>
<a-slider :marks="marks" :defaultValue="37" />
<a-slider range :marks="marks" :defaultValue="[26, 37]" />
<h4>included=false</h4>
<a-slider :marks="marks" :included="false" :defaultValue="37" />
<h4>marks & step</h4>
<a-slider :marks="marks" :step="10" :defaultValue="37" />
<h4>step=null</h4>
<a-slider :marks="marks" :step="null" :defaultValue="37" />
</div>
</template>
<script>
export default {
data() {
return {
marks: {
0: '0°C',
26: '26°C',
37: '37°C',
100: {
style: {
color: '#f50',
},
label: <strong>100°C</strong>,
},
},
}
},
methods: {
onChange(value) {
console.log('change: ', value);
},
onAfterChange(value) {
console.log('afterChange: ', value);
}
},
}
</script>
<style scoped>
#components-slider-demo-mark h4 {
margin: 0 0 16px;
}
#components-slider-demo-mark .ant-slider-with-marks {
margin-bottom: 44px;
}
</style>
```

View File

@ -0,0 +1,33 @@
<cn>
#### 自定义提示
使用 `tipFormatter` 可以格式化 `Tooltip` 的内容,设置 `tipFormatter={null}`,则隐藏 `Tooltip`
</cn>
<us>
#### Customerize tooltip
Use `tipFormatter` to format content of `Toolip`. If `tipFormatter` is null, hide it.
</us>
```html
<template>
<div>
<a-slider :tipFormatter="formatter" />
<a-slider :tipFormatter="null" />
</div>
</template>
<script>
export default {
data() {
return {
disabled: false,
}
},
methods: {
formatter(value) {
return `${value}%`;
}
},
}
</script>
```

View File

@ -0,0 +1,55 @@
<cn>
#### 垂直
垂直方向的 Slider。
</cn>
<us>
#### Vertical
The vertical Slider.
</us>
```html
<template>
<div style="height: 300px">
<div style="float:left;height: 300px;marginLeft: 70px">
<a-slider vertical :defaultValue="30" />
</div>
<div style="float:left;height: 300px;marginLeft: 70px">
<a-slider vertical range :step="10" :defaultValue="[20, 50]" />
</div>
<div style="float:left;height: 300px;marginLeft: 70px">
<a-slider vertical range :marks="marks" :defaultValue="[26, 37]" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
marks: {
0: '0°C',
26: '26°C',
37: '37°C',
100: {
style: {
color: '#f50',
},
label: <strong>100°C</strong>,
},
},
}
},
methods: {
handleDisabledChange(disabled) {
this.disabled = disabled
}
},
}
</script>
<style scoped>
.code-box-demo .ant-slider {
margin-bottom: 16px;
}
</style>
```

View File

@ -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 |

145
components/slider/index.jsx Normal file
View File

@ -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 (
<Tooltip
{...tooltipProps}
>
<VcHandle
{...handleProps}
/>
</Tooltip>
)
},
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 <VcRange {...vcRangeProps} />
}
const vcSliderProps = {
props: {
handle: this.handleWithTooltip,
...restProps,
},
ref: 'sliderRef',
on: this.$listeners,
}
return <VcSlider {...vcSliderProps} />
},
}

View File

@ -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() | 获取焦点 |

View File

@ -0,0 +1,5 @@
import '../../style/index.less'
import './index.less'
// style dependencies
import '../../tooltip/style'

View File

@ -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;
}
}
}

View File

@ -34,3 +34,4 @@ import './steps/style'
import './breadcrumb/style'
import './calendar/style'
import './date-picker/style'
import './slider/style'

View File

@ -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

View File

@ -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)
},

View File

@ -3,7 +3,7 @@ const AsyncComp = () => {
const hashs = window.location.hash.split('/')
const d = hashs[hashs.length - 1]
return {
component: import(`../components/vc-table/demo/${d}`),
component: import(`../components/slider/demo/${d}`),
}
}
export default [