diff --git a/docs/demos/index.js b/docs/demos/index.js index 2403dd8a1..741a1a801 100644 --- a/docs/demos/index.js +++ b/docs/demos/index.js @@ -59,6 +59,7 @@ export default { 'radio': asyncWrapper(r => require.ensure([], () => r(componentWrapper(require('./views/radio'), 'radio')), 'radio')), 'search': asyncWrapper(r => require.ensure([], () => r(componentWrapper(require('./views/search'), 'search')), 'search')), 'sku': asyncWrapper(r => require.ensure([], () => r(componentWrapper(require('./views/sku'), 'sku')), 'sku')), + 'slider': asyncWrapper(r => require.ensure([], () => r(componentWrapper(require('./views/slider'), 'slider')), 'slider')), 'stepper': asyncWrapper(r => require.ensure([], () => r(componentWrapper(require('./views/stepper'), 'stepper')), 'stepper')), 'steps': asyncWrapper(r => require.ensure([], () => r(componentWrapper(require('./views/steps'), 'steps')), 'steps')), 'submit-bar': asyncWrapper(r => require.ensure([], () => r(componentWrapper(require('./views/submit-bar'), 'submit-bar')), 'submit-bar')), diff --git a/docs/demos/views/slider.vue b/docs/demos/views/slider.vue new file mode 100644 index 000000000..0a0e07b4a --- /dev/null +++ b/docs/demos/views/slider.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/docs/markdown/en-US/slider.md b/docs/markdown/en-US/slider.md new file mode 100644 index 000000000..9c6af4378 --- /dev/null +++ b/docs/markdown/en-US/slider.md @@ -0,0 +1,109 @@ +## Slider + +### Install +``` javascript +import { Slider } from 'vant'; + +Vue.use(Slider); +``` + +### Usage +#### Basic Usage + +```html + + + + + + +``` + +```js +data() { + return { + value1: 50 + } +} +``` + +#### Max && Min + +```html + +``` +```js +data() { + return { + value2: 50, + min: 10, + max: 90 + } +} +``` + +#### Disabed + +```html + +``` + +#### Customized style + +```html + +``` + +```js +data() { + return { + value4: 50 + } +}, +methods: { + handleChange(value) { + console.log('handleChange:', value) + }, + handleAfterChange(value) { + console.log('handleAfterChange:', value) + } +} +``` + +### Customized style + +```html + +``` + +### API + +| Attribute | Description | Type | Default | Accepted Values | +|-----------|-----------|-----------|-------------|-------------| +| value | current value | Number | 0 | - | +| disabled | disabled | Boolean | false | - | +| bar-color | bar-color | string | `#cacaca` | - | +| loaded-bar-color | loaded-bar-color | string | `#4b0` | - | +| pivot-color | pivot-color | string | `#4b0` | - | +| max | max | Number | 100 | - | +| min | min | Number | 0 | - | + +### Event + +| Event | Description | Arguments | +|-----------|-----------|-----------| +| change | touchmove emit | value | +| after-change | touchend emit | value | diff --git a/docs/markdown/index.js b/docs/markdown/index.js index 76f0fb952..e5ba2075c 100644 --- a/docs/markdown/index.js +++ b/docs/markdown/index.js @@ -56,6 +56,7 @@ export default { 'zh-CN/radio': wrapper(r => require.ensure([], () => r(require('./zh-CN/radio.md')), 'zh-CN/radio')), 'zh-CN/search': wrapper(r => require.ensure([], () => r(require('./zh-CN/search.md')), 'zh-CN/search')), 'zh-CN/sku': wrapper(r => require.ensure([], () => r(require('./zh-CN/sku.md')), 'zh-CN/sku')), + 'zh-CN/slider': wrapper(r => require.ensure([], () => r(require('./zh-CN/slider.md')), 'zh-CN/slider')), 'zh-CN/stepper': wrapper(r => require.ensure([], () => r(require('./zh-CN/stepper.md')), 'zh-CN/stepper')), 'zh-CN/steps': wrapper(r => require.ensure([], () => r(require('./zh-CN/steps.md')), 'zh-CN/steps')), 'zh-CN/submit-bar': wrapper(r => require.ensure([], () => r(require('./zh-CN/submit-bar.md')), 'zh-CN/submit-bar')), @@ -112,6 +113,7 @@ export default { 'en-US/radio': wrapper(r => require.ensure([], () => r(require('./en-US/radio.md')), 'en-US/radio')), 'en-US/search': wrapper(r => require.ensure([], () => r(require('./en-US/search.md')), 'en-US/search')), 'en-US/sku': wrapper(r => require.ensure([], () => r(require('./en-US/sku.md')), 'en-US/sku')), + 'en-US/slider': wrapper(r => require.ensure([], () => r(require('./en-US/slider.md')), 'en-US/slider')), 'en-US/stepper': wrapper(r => require.ensure([], () => r(require('./en-US/stepper.md')), 'en-US/stepper')), 'en-US/steps': wrapper(r => require.ensure([], () => r(require('./en-US/steps.md')), 'en-US/steps')), 'en-US/submit-bar': wrapper(r => require.ensure([], () => r(require('./en-US/submit-bar.md')), 'en-US/submit-bar')), diff --git a/docs/markdown/zh-CN/slider.md b/docs/markdown/zh-CN/slider.md new file mode 100644 index 000000000..5cffc243c --- /dev/null +++ b/docs/markdown/zh-CN/slider.md @@ -0,0 +1,108 @@ +## Slider 滑块 + +### 使用指南 +``` javascript +import { Slider } from 'vant'; + +Vue.use(Slider); +``` + +#### 基本用法 + +```html + + + + + + +``` + +```js +data() { + return { + value1: 50 + } +} +``` + +#### 最大最小值 + +```html + +``` +```js +data() { + return { + value2: 50, + min: 10, + max: 90 + } +} +``` + +#### 禁用 + +```html + +``` + +#### 事件 + +```html + +``` + +```js +data() { + return { + value4: 50 + } +}, +methods: { + handleChange(value) { + console.log('handleChange:', value) + }, + handleAfterChange(value) { + console.log('handleAfterChange:', value) + } +} +``` + +### 自定义样式 + +```html + +``` + +### API + +| 参数 | 说明 | 类型 | 默认值 | 必须 | +|-----------|-----------|-----------|-------------|-------------| +| value | 当前进度百分比 | Number | 0 | 否 | +| disabled | 滑块是否禁用 | Boolean | false | 否 | +| bar-color | 进度条颜色 | string | `#cacaca` | 否 | +| loaded-bar-color | 已加载条颜色 | string | `#4b0` | 否 | +| pivot-color | 滑块颜色 | string | `#4b0` | 否 | +| max | 最大值 | Number | 100 | - | +| min | 最小值 | Number | 0 | - | + +### Event + +| 事件名 | 说明 | 参数 | +|-----------|-----------|-----------| +| change | 拖动时触发 | value | +| after-change | 拖动停止后触发 | value | diff --git a/docs/src/doc.config.js b/docs/src/doc.config.js index 3cfdb6560..8117af688 100644 --- a/docs/src/doc.config.js +++ b/docs/src/doc.config.js @@ -148,6 +148,10 @@ module.exports = { path: '/swipe', title: 'Swipe - 轮播' }, + { + path: '/slider', + title: 'Slider - 滑块' + }, { path: '/tab', title: 'Tab - 标签页' @@ -442,6 +446,10 @@ module.exports = { path: '/swipe', title: 'Swipe' }, + { + path: '/slider', + title: 'Slider' + }, { path: '/tab', title: 'Tab' diff --git a/packages/index.js b/packages/index.js index e34632cd7..131c6d6b2 100644 --- a/packages/index.js +++ b/packages/index.js @@ -48,6 +48,7 @@ import RadioGroup from './radio-group'; import Row from './row'; import Search from './search'; import Sku from './sku'; +import Slider from './slider'; import Step from './step'; import Stepper from './stepper'; import Steps from './steps'; @@ -116,6 +117,7 @@ const components = [ Row, Search, Sku, + Slider, Step, Stepper, Steps, @@ -196,6 +198,7 @@ export { Row, Search, Sku, + Slider, Step, Stepper, Steps, diff --git a/packages/slider/index.vue b/packages/slider/index.vue new file mode 100644 index 000000000..2d042cea1 --- /dev/null +++ b/packages/slider/index.vue @@ -0,0 +1,170 @@ + + diff --git a/packages/vant-css/src/index.css b/packages/vant-css/src/index.css index 15c427c70..269044633 100644 --- a/packages/vant-css/src/index.css +++ b/packages/vant-css/src/index.css @@ -26,6 +26,7 @@ @import './stepper.css'; @import './progress.css'; @import './swipe.css'; +@import './slider.css'; /* form components */ @import './checkbox.css'; diff --git a/packages/vant-css/src/slider.css b/packages/vant-css/src/slider.css new file mode 100644 index 000000000..888afcffd --- /dev/null +++ b/packages/vant-css/src/slider.css @@ -0,0 +1,41 @@ +@import './common/var.css'; + +$c-slider-background: #cacaca; +$c-pivot-border: #fff; + +.van-slider { + &--disabled { + opacity: .3; + .van-slider__pivot { + cursor: not-allowed; + } + } + + &__bar { + position: relative; + margin: 0 15px; + height: 4px; + border-radius: 5px; + background: $c-slider-background; + } + + &__loaded-portion, + &__finished-portion { + border-radius: 5px; + height: 100%; + position: absolute; + left: 0; + } + + &__pivot { + position: absolute; + top: 50%; + width: 16px; + height: 16px; + border: 3px solid $c-pivot-border; + box-shadow: 0 1px 4px; + border-radius: 50%; + background-color: $c-slider-background; + transform: translate3d(0, -50%, 0); + } +} diff --git a/test/specs/slider.spec.js b/test/specs/slider.spec.js new file mode 100644 index 000000000..8287bca39 --- /dev/null +++ b/test/specs/slider.spec.js @@ -0,0 +1,98 @@ +import Slider from 'packages/slider'; +import { mount } from 'avoriaz'; +import { triggerTouch } from '../utils'; + +describe('Slider', () => { + let wrapper; + + afterEach(() => { + wrapper && wrapper.destroy(); + }); + + it('create a simple slider', () => { + wrapper = mount(Slider, { + propsData: { + value: 50, + disabled: true + } + }); + + expect(wrapper.hasClass('van-slider')).to.be.true; + expect(wrapper.find('.van-slider__bar').length).to.equal(1); + expect(wrapper.find('.van-slider__finished-portion').length).to.equal(1); + expect(wrapper.vm.value).to.equal(50); + expect(wrapper.hasClass('van-slider--disabled')).to.equal(true); + + wrapper.setProps({ + value: 100 + }); + wrapper.update(); + + expect(wrapper.vm.value).to.equal(100); + }); + + it('test click bar', () => { + wrapper = mount(Slider, { + propsData: { + disabled: true, + value: 50 + } + }); + + const eventStub = sinon.stub(wrapper.vm, '$emit'); + const $bar = wrapper.find('.van-slider__bar')[0]; + $bar.trigger('click'); + + expect(eventStub.called).to.equal(false); + + wrapper.setData({ + disabled: false + }); + wrapper.update(); + $bar.trigger('click'); + + expect(wrapper.vm.disabled).to.equal(false); + expect(eventStub.called).to.equal(true); + }); + + it('Customized style', () => { + const COLOR = '#123'; + wrapper = mount(Slider, { + propsData: { + pivotColor: COLOR, + barColor: COLOR, + loadedBarColor: COLOR, + value: 50 + } + }); + + expect(wrapper.vm.barStyle.backgroundColor).to.equal(COLOR); + expect(wrapper.vm.pivotStyle.backgroundColor).to.equal(COLOR); + expect(wrapper.vm.finishedStyle.backgroundColor).to.equal(COLOR); + }); + + it('drag pivot', () => { + wrapper = mount(Slider, { + propsData: { + value: 50 + } + }); + + const pivot = wrapper.find('.van-slider__pivot')[0]; + triggerTouch(pivot, 'touchstart', 0, 0); + expect(wrapper.vm.startX).to.equal(0); + + triggerTouch(pivot, 'touchmove', 50, 0); + expect(wrapper.vm.offsetX).to.equal(50); + + triggerTouch(pivot, 'touchend', 50, 0); + expect(wrapper.vm.offsetX).to.equal(50); + + wrapper.setData({ + disabled: true + }); + + triggerTouch(pivot, 'touchstart', 0, 0); + expect(wrapper.vm.startX).to.equal(0); + }); +});