diff --git a/.jest.js b/.jest.js
index db17b25701..fa5d4800aa 100644
--- a/.jest.js
+++ b/.jest.js
@@ -14,7 +14,8 @@ module.exports = {
"jsx",
"json",
"vue",
- "md"
+ "md",
+ "jpg"
],
modulePathIgnorePatterns: [
'/_site/',
diff --git a/components/carousel/__tests__/__snapshots__/demo.test.js.snap b/components/carousel/__tests__/__snapshots__/demo.test.js.snap
new file mode 100644
index 0000000000..bae53c1893
--- /dev/null
+++ b/components/carousel/__tests__/__snapshots__/demo.test.js.snap
@@ -0,0 +1,318 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders ./components/carousel/demo/autoplay.md correctly 1`] = `
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+`;
+
+exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+`;
+
+exports[`renders ./components/carousel/demo/fade.md correctly 1`] = `
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+`;
+
+exports[`renders ./components/carousel/demo/vertical.md correctly 1`] = `
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+`;
diff --git a/components/carousel/__tests__/demo.test.js b/components/carousel/__tests__/demo.test.js
new file mode 100644
index 0000000000..526e3d1e02
--- /dev/null
+++ b/components/carousel/__tests__/demo.test.js
@@ -0,0 +1,3 @@
+import demoTest from '../../../tests/shared/demoTest'
+
+demoTest('carousel', { skip: ['customPaging.md'] })
diff --git a/components/carousel/__tests__/index.test.js b/components/carousel/__tests__/index.test.js
new file mode 100644
index 0000000000..03e17e5fb9
--- /dev/null
+++ b/components/carousel/__tests__/index.test.js
@@ -0,0 +1,87 @@
+import { mount } from '@vue/test-utils'
+import { asyncExpect } from '@/tests/utils'
+import Carousel from '..'
+
+describe('Carousel', () => {
+ it('should has innerSlider', () => {
+ const props = {
+ slots: {
+ default: '',
+ },
+ sync: true,
+ }
+ const wrapper = mount(Carousel, props)
+ const { innerSlider, $refs } = wrapper.vm
+ const innerSliderFromRefs = $refs.slick.innerSlider
+ expect(innerSlider).toBe(innerSliderFromRefs)
+ expect(typeof innerSlider.slickNext).toBe('function')
+ })
+
+ it('should has prev, next and go function', async () => {
+ const props = {
+ slots: {
+ default: '1
2
3
',
+ },
+ sync: true,
+ }
+ const wrapper = mount(Carousel, props)
+ const { prev, next, goTo } = wrapper.vm
+ expect(typeof prev).toBe('function')
+ expect(typeof next).toBe('function')
+ expect(typeof goTo).toBe('function')
+ const slick = wrapper.vm.$refs.slick
+
+ expect(slick.innerSlider.currentSlide).toBe(0)
+ wrapper.vm.goTo(2)
+ await asyncExpect(() => {
+ expect(slick.innerSlider.currentSlide).toBe(2)
+ }, 1000)
+ prev()
+ await asyncExpect(() => {
+ expect(slick.innerSlider.currentSlide).toBe(1)
+ }, 1000)
+
+ next()
+
+ await asyncExpect(() => {
+ expect(slick.innerSlider.currentSlide).toBe(2)
+ }, 1000)
+ })
+
+ it('should trigger autoPlay after window resize', async () => {
+ const props = {
+ propsData: {
+ autoplay: true,
+ },
+ slots: {
+ default: '1
2
3
',
+ },
+ sync: true,
+ }
+ const wrapper = mount(Carousel, props)
+ const spy = jest.spyOn(wrapper.vm.$refs.slick.innerSlider, 'handleAutoPlay')
+ window.resizeTo(1000)
+ expect(spy).not.toBeCalled()
+ await new Promise(resolve => setTimeout(resolve, 1000))
+ expect(spy).toBeCalled()
+ })
+
+ it('cancel resize listener when unmount', async () => {
+ const props = {
+ propsData: {
+ autoplay: true,
+ },
+ slots: {
+ default: '1
2
3
',
+ },
+ sync: true,
+ }
+ const wrapper = mount(Carousel, props)
+ const onWindowResized = wrapper.vm.onWindowResized
+ const spy = jest.spyOn(wrapper.vm.onWindowResized, 'cancel')
+ const spy2 = jest.spyOn(window, 'removeEventListener')
+ wrapper.destroy()
+ expect(spy).toBeCalled()
+ expect(spy2).toBeCalledWith('resize', onWindowResized)
+ })
+})
diff --git a/components/carousel/demo/autoplay.md b/components/carousel/demo/autoplay.md
new file mode 100644
index 0000000000..481973dd54
--- /dev/null
+++ b/components/carousel/demo/autoplay.md
@@ -0,0 +1,38 @@
+
+#### 自动切换
+定时切换下一张。
+
+
+
+#### Scroll automatically
+Timing of scrolling to the next card/picture.
+
+
+```html
+
+
+ 1
+ 2
+ 3
+ 4
+
+
+
+
+```
diff --git a/components/carousel/demo/basic.md b/components/carousel/demo/basic.md
new file mode 100644
index 0000000000..fae371f12f
--- /dev/null
+++ b/components/carousel/demo/basic.md
@@ -0,0 +1,43 @@
+
+#### 基本
+最简单的用法。
+
+
+
+#### Basic
+Basic usage.
+
+
+```html
+
+
+ 1
+ 2
+ 3
+ 4
+
+
+
+
+```
diff --git a/components/carousel/demo/customPaging.md b/components/carousel/demo/customPaging.md
new file mode 100644
index 0000000000..2e9b3c71a5
--- /dev/null
+++ b/components/carousel/demo/customPaging.md
@@ -0,0 +1,73 @@
+
+#### 自定义分页
+自定义分页展示。
+
+
+
+#### Custom Paging
+Custom paging display
+
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/components/carousel/demo/fade.md b/components/carousel/demo/fade.md
new file mode 100644
index 0000000000..8271951899
--- /dev/null
+++ b/components/carousel/demo/fade.md
@@ -0,0 +1,38 @@
+
+#### 渐显
+切换效果为渐显。
+
+
+
+#### Fade in
+Slides use fade for transition.
+
+
+```html
+
+
+ 1
+ 2
+ 3
+ 4
+
+
+
+
+```
diff --git a/components/carousel/demo/index.vue b/components/carousel/demo/index.vue
new file mode 100644
index 0000000000..4617aef870
--- /dev/null
+++ b/components/carousel/demo/index.vue
@@ -0,0 +1,53 @@
+
diff --git a/components/carousel/demo/vertical.md b/components/carousel/demo/vertical.md
new file mode 100644
index 0000000000..9950956dc3
--- /dev/null
+++ b/components/carousel/demo/vertical.md
@@ -0,0 +1,38 @@
+
+#### 垂直
+垂直显示。
+
+
+
+#### Vertical
+Vertical pagination.
+
+
+```html
+
+
+ 1
+ 2
+ 3
+ 4
+
+
+
+
+```
diff --git a/components/carousel/index.en-US.md b/components/carousel/index.en-US.md
new file mode 100644
index 0000000000..bad1694679
--- /dev/null
+++ b/components/carousel/index.en-US.md
@@ -0,0 +1,21 @@
+## API
+
+| Property | Description | Type | Default |
+| -------- | ----------- | ---- | ------- |
+| afterChange | Callback function called after the current index changes | function(current) | - |
+| autoplay | Whether to scroll automatically | boolean | `false` |
+| beforeChange | Callback function called before the current index changes | function(from, to) | - |
+| dots | Whether to show the dots at the bottom of the gallery | boolean | `true` |
+| easing | Transition interpolation function name | string | `linear` |
+| effect | Transition effect | `scrollx` \| `fade` | `scrollx` |
+| vertical | Whether to use a vertical display | boolean | `false` |
+
+## Methods
+
+| Name | Description |
+| ---- | ----------- |
+| goTo(slideNumber) | Change current slide to given slide number |
+| next() | Change current slide to next slide |
+| prev() | Change current slide to previous slide |
+
+For more info on the parameters, refer to the
diff --git a/components/carousel/index.jsx b/components/carousel/index.jsx
new file mode 100644
index 0000000000..4e97cf5ec2
--- /dev/null
+++ b/components/carousel/index.jsx
@@ -0,0 +1,159 @@
+import PropTypes from '../_util/vue-types'
+import debounce from 'lodash/debounce'
+import { initDefaultProps, getComponentFromProp, filterEmpty } from '../_util/props-util'
+
+// matchMedia polyfill for
+// https://github.com/WickyNilliams/enquire.js/issues/82
+if (typeof window !== 'undefined') {
+ const matchMediaPolyfill = (mediaQuery) => {
+ return {
+ media: mediaQuery,
+ matches: false,
+ addListener () {
+ },
+ removeListener () {
+ },
+ }
+ }
+ window.matchMedia = window.matchMedia || matchMediaPolyfill
+}
+// Use require over import (will be lifted up)
+// make sure matchMedia polyfill run before require('vc-slick')
+// Fix https://github.com/ant-design/ant-design/issues/6560
+// Fix https://github.com/ant-design/ant-design/issues/3308
+const SlickCarousel = require('../vc-slick/src').default
+
+export const CarouselEffect = PropTypes.oneOf(['scrollx', 'fade'])
+// Carousel
+export const CarouselProps = {
+ effect: CarouselEffect,
+ dots: PropTypes.bool,
+ vertical: PropTypes.bool,
+ autoplay: PropTypes.bool,
+ easing: PropTypes.string,
+ beforeChange: PropTypes.func,
+ afterChange: PropTypes.func,
+ // style: PropTypes.React.CSSProperties,
+ prefixCls: PropTypes.string,
+ accessibility: PropTypes.bool,
+ nextArrow: PropTypes.any,
+ prevArrow: PropTypes.any,
+ pauseOnHover: PropTypes.bool,
+ // className: PropTypes.string,
+ adaptiveHeight: PropTypes.bool,
+ arrows: PropTypes.bool,
+ autoplaySpeed: PropTypes.number,
+ centerMode: PropTypes.bool,
+ centerPadding: PropTypes.string,
+ cssEase: PropTypes.string,
+ dotsClass: PropTypes.string,
+ draggable: PropTypes.bool,
+ fade: PropTypes.bool,
+ focusOnSelect: PropTypes.bool,
+ infinite: PropTypes.bool,
+ initialSlide: PropTypes.number,
+ lazyLoad: PropTypes.bool,
+ rtl: PropTypes.bool,
+ slide: PropTypes.string,
+ slidesToShow: PropTypes.number,
+ slidesToScroll: PropTypes.number,
+ speed: PropTypes.number,
+ swipe: PropTypes.bool,
+ swipeToSlide: PropTypes.bool,
+ touchMove: PropTypes.bool,
+ touchThreshold: PropTypes.number,
+ variableWidth: PropTypes.bool,
+ useCSS: PropTypes.bool,
+ slickGoTo: PropTypes.number,
+}
+
+export default {
+ name: 'ACarousel',
+ props: initDefaultProps(CarouselProps, {
+ dots: true,
+ arrows: false,
+ prefixCls: 'ant-carousel',
+ draggable: false,
+ }),
+
+ // innerSlider: any;
+
+ // private slick: any;
+
+ beforeMount () {
+ this.onWindowResized = debounce(this.onWindowResized, 500, {
+ leading: false,
+ })
+ },
+
+ mounted () {
+ const { autoplay } = this
+ if (autoplay) {
+ window.addEventListener('resize', this.onWindowResized)
+ }
+ // https://github.com/ant-design/ant-design/issues/7191
+ this.innerSlider = this.$refs.slick && this.$refs.slick.innerSlider
+ },
+
+ beforeDestroy () {
+ const { autoplay } = this
+ if (autoplay) {
+ window.removeEventListener('resize', this.onWindowResized)
+ this.onWindowResized.cancel()
+ }
+ },
+ methods: {
+ onWindowResized () {
+ // Fix https://github.com/ant-design/ant-design/issues/2550
+ const { autoplay } = this
+ if (autoplay && this.$refs.slick && this.$refs.slick.innerSlider && this.$refs.slick.innerSlider.autoPlay) {
+ this.$refs.slick.innerSlider.autoPlay()
+ }
+ },
+
+ next () {
+ this.$refs.slick.slickNext()
+ },
+
+ prev () {
+ this.$refs.slick.slickPrev()
+ },
+
+ goTo (slide) {
+ this.$refs.slick.slickGoTo(slide)
+ },
+ },
+
+ render () {
+ const props = {
+ ...this.$props,
+ }
+ const { $slots, $listeners } = this
+
+ if (props.effect === 'fade') {
+ props.fade = true
+ }
+
+ let className = props.prefixCls
+ if (props.vertical) {
+ className = `${className} ${className}-vertical`
+ }
+ const SlickCarouselProps = {
+ props: {
+ ...props,
+ nextArrow: getComponentFromProp(this, 'nextArrow'),
+ prevArrow: getComponentFromProp(this, 'prevArrow'),
+ },
+ on: $listeners,
+ scopedSlots: this.$scopedSlots,
+ }
+
+ return (
+
+
+ {filterEmpty($slots.default)}
+
+
+ )
+ },
+}
diff --git a/components/carousel/index.zh-CN.md b/components/carousel/index.zh-CN.md
new file mode 100644
index 0000000000..a5dc70a428
--- /dev/null
+++ b/components/carousel/index.zh-CN.md
@@ -0,0 +1,21 @@
+## API
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| afterChange | 切换面板的回调 | function(current) | 无 |
+| autoplay | 是否自动切换 | boolean | false |
+| beforeChange | 切换面板的回调 | function(from, to) | 无 |
+| dots | 是否显示面板指示点 | boolean | true |
+| easing | 动画效果 | string | linear |
+| effect | 动画效果函数,可取 scrollx, fade | string | scrollx |
+| vertical | 垂直显示 | boolean | false |
+
+## 方法
+
+| 名称 | 描述 |
+| --- | --- |
+| goTo(slideNumber) | 切换到指定面板 |
+| next() | 切换到下一面板 |
+| prev() | 切换到上一面板 |
+
+更多参数可参考:
diff --git a/components/carousel/style/index.js b/components/carousel/style/index.js
new file mode 100644
index 0000000000..cf31ed80fb
--- /dev/null
+++ b/components/carousel/style/index.js
@@ -0,0 +1,2 @@
+import '../../style/index.less'
+import './index.less'
diff --git a/components/carousel/style/index.less b/components/carousel/style/index.less
new file mode 100644
index 0000000000..613f42bcb3
--- /dev/null
+++ b/components/carousel/style/index.less
@@ -0,0 +1,210 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+
+.@{ant-prefix}-carousel {
+ .reset-component;
+
+ .slick-slider {
+ position: relative;
+ display: block;
+ box-sizing: border-box;
+ -webkit-touch-callout: none;
+ -ms-touch-action: pan-y;
+ touch-action: pan-y;
+ -webkit-tap-highlight-color: transparent;
+ }
+ .slick-list {
+ position: relative;
+ overflow: hidden;
+ display: block;
+ margin: 0;
+ padding: 0;
+
+ &:focus {
+ outline: none;
+ }
+
+ &.dragging {
+ cursor: pointer;
+ }
+ }
+ .slick-slider .slick-track,
+ .slick-slider .slick-list {
+ transform: translate3d(0, 0, 0);
+ }
+
+ .slick-track {
+ position: relative;
+ left: 0;
+ top: 0;
+ display: block;
+
+ &:before,
+ &:after {
+ content: "";
+ display: table;
+ }
+
+ &:after {
+ clear: both;
+ }
+
+ .slick-loading & {
+ visibility: hidden;
+ }
+ }
+ .slick-slide {
+ float: left;
+ height: 100%;
+ min-height: 1px;
+ [dir="rtl"] & {
+ float: right;
+ }
+ img {
+ display: block;
+ }
+ &.slick-loading img {
+ display: none;
+ }
+
+ display: none;
+
+ &.dragging img {
+ pointer-events: none;
+ }
+ }
+
+ .slick-initialized .slick-slide {
+ display: block;
+ }
+
+ .slick-loading .slick-slide {
+ visibility: hidden;
+ }
+
+ .slick-vertical .slick-slide {
+ display: block;
+ height: auto;
+ border: @border-width-base @border-style-base transparent;
+ }
+ .slick-arrow.slick-hidden {
+ display: none;
+ }
+
+ // Arrows
+ .slick-prev,
+ .slick-next {
+ position: absolute;
+ display: block;
+ height: 20px;
+ width: 20px;
+ line-height: 0;
+ font-size: 0;
+ cursor: pointer;
+ background: transparent;
+ color: transparent;
+ top: 50%;
+ margin-top: -10px;
+ padding: 0;
+ border: 0;
+ outline: none;
+ &:hover,
+ &:focus {
+ outline: none;
+ background: transparent;
+ color: transparent;
+ &:before {
+ opacity: 1;
+ }
+ }
+ &.slick-disabled:before {
+ opacity: 0.25;
+ }
+ }
+
+ .slick-prev {
+ left: -25px;
+ &:before {
+ content: "←";
+ }
+ }
+
+ .slick-next {
+ right: -25px;
+ &:before {
+ content: "→";
+ }
+ }
+
+ // Dots
+ .slick-dots {
+ position: absolute;
+ bottom: 12px;
+ list-style: none;
+ display: block;
+ text-align: center;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: @carousel-dot-height;
+ li {
+ position: relative;
+ display: inline-block;
+ vertical-align: top;
+ text-align: center;
+ margin: 0 2px;
+ padding: 0;
+ button {
+ border: 0;
+ cursor: pointer;
+ background: #fff;
+ opacity: 0.3;
+ display: block;
+ width: @carousel-dot-width;
+ height: @carousel-dot-height;
+ border-radius: 1px;
+ outline: none;
+ font-size: 0;
+ color: transparent;
+ transition: all .5s;
+ padding: 0;
+ &:hover,
+ &:focus {
+ opacity: 0.75;
+ }
+ }
+ &.slick-active button {
+ background: #fff;
+ opacity: 1;
+ width: @carousel-dot-active-width;
+ &:hover,
+ &:focus {
+ opacity: 1;
+ }
+ }
+ }
+ }
+}
+
+.@{ant-prefix}-carousel-vertical {
+ .slick-dots {
+ width: @carousel-dot-height;
+ bottom: auto;
+ right: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+ height: auto;
+ li {
+ margin: 0 2px;
+ vertical-align: baseline;
+ button {
+ width: @carousel-dot-height;
+ height: @carousel-dot-width;
+ }
+ &.slick-active button {
+ width: @carousel-dot-height;
+ height: @carousel-dot-active-width;
+ }
+ }
+ }
+}
diff --git a/components/index.js b/components/index.js
index 5e6fce8f86..e8a5d4a2c7 100644
--- a/components/index.js
+++ b/components/index.js
@@ -36,7 +36,7 @@ import { default as Card } from './card'
import { default as Collapse } from './collapse'
-// import { default as Carousel } from './carousel'
+import { default as Carousel } from './carousel'
import { default as Cascader } from './cascader'
@@ -139,6 +139,7 @@ const components = [
Card.Grid,
Collapse,
Collapse.Panel,
+ Carousel,
Cascader,
Checkbox,
Checkbox.Group,
@@ -241,6 +242,7 @@ export {
Calendar,
Card,
Collapse,
+ Carousel,
Cascader,
Checkbox,
Col,
diff --git a/components/style.js b/components/style.js
index 4169397709..4c2d945b20 100644
--- a/components/style.js
+++ b/components/style.js
@@ -18,6 +18,7 @@ import './dropdown/style'
import './divider/style'
import './card/style'
import './collapse/style'
+import './carousel/style'
import './notification/style'
import './message/style'
import './spin/style'
@@ -46,4 +47,5 @@ import './layout/style'
import './form/style'
import './anchor/style'
import './list/style'
+import './carousel/style'
import './tree-select/style'
diff --git a/components/vc-slick/assets/ajax-loader.gif b/components/vc-slick/assets/ajax-loader.gif
new file mode 100644
index 0000000000..e0e6e9760b
Binary files /dev/null and b/components/vc-slick/assets/ajax-loader.gif differ
diff --git a/components/vc-slick/assets/docs.less b/components/vc-slick/assets/docs.less
new file mode 100644
index 0000000000..5e9bad5a1b
--- /dev/null
+++ b/components/vc-slick/assets/docs.less
@@ -0,0 +1,116 @@
+
+h3 {
+ background: #00558B;
+ color: #fff;
+ font-size: 36px;
+ line-height: 100px;
+ margin: 10px;
+ padding: 2%;
+ position: relative;
+ text-align: center;
+}
+.variable-width .slick-slide p {
+ background: #00558B;
+ height: 100px;
+ color: #fff;
+ margin: 5px;
+ line-height: 100px;
+ text-align: center;
+}
+.center .slick-center h3 {
+ color: #e67e22;
+ opacity: 1;
+ transform: scale(1.08);
+}
+.center h3{
+ opacity: 0.8;
+ transition: all 300ms ease;
+}
+.content {
+ padding: 20px;
+ margin: auto;
+}
+@media (min-width: 701px) {
+ .content {
+ width: 80%;
+ }
+}
+@media (max-width: 700px) {
+ .content {
+ width: 70%;
+ }
+}
+.slick-slide .image {
+ padding: 10px;
+}
+.slick-slide img {
+ border: 5px solid #FFF;
+ display: block;
+ margin: auto;
+ max-width: 80%;
+}
+.slick-slide img.slick-loading {
+ border: 0
+}
+.slick-slider {
+ margin: 30px auto 50px;
+}
+.slick-dots {
+ margin-left: 0;
+}
+.slick-thumb {
+ bottom: -45px;
+}
+.slick-thumb li {
+ width: 60px;
+ height: 45px;
+}
+.slick-thumb li img {
+ width: 100%;
+ height: 100%;
+ filter: grayscale(100%);
+}
+.slick-thumb li.slick-active img{
+ filter: grayscale(0%);
+}
+@media (max-width: 768px) {
+ h3 {
+ font-size:24px;
+ }
+ .center {
+ margin-left: -40px;
+ margin-right: -40px;
+ }
+ .center .slick-center h3 {
+ color: #e67e22;
+ opacity: 1;
+ transform: scale(1);
+ }
+ .center h3 {
+ opacity: 0.8;
+ transform: scale(0.95);
+ transition: all 300ms ease;
+ }
+}
+.slick-vertical .slick-slide {
+ height: 180px;
+}
+.slick-arrow {
+ background-color: grey;
+}
+.slick-arrow:hover {
+ background-color: grey;
+}
+.slick-arrow:focus {
+ background-color: grey;
+}
+.button {
+ background-color: #00558B;
+ padding: 10px 20px;
+ margin: 0px 20px;
+ border: none;
+ color: white;
+ font-size: 20px;
+ border-radius: 5px;
+ min-height: 45px
+}
diff --git a/components/vc-slick/assets/fonts/slick.eot b/components/vc-slick/assets/fonts/slick.eot
new file mode 100644
index 0000000000..2cbab9ca97
Binary files /dev/null and b/components/vc-slick/assets/fonts/slick.eot differ
diff --git a/components/vc-slick/assets/fonts/slick.svg b/components/vc-slick/assets/fonts/slick.svg
new file mode 100644
index 0000000000..b36a66a6c4
--- /dev/null
+++ b/components/vc-slick/assets/fonts/slick.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/components/vc-slick/assets/fonts/slick.ttf b/components/vc-slick/assets/fonts/slick.ttf
new file mode 100644
index 0000000000..9d03461b65
Binary files /dev/null and b/components/vc-slick/assets/fonts/slick.ttf differ
diff --git a/components/vc-slick/assets/fonts/slick.woff b/components/vc-slick/assets/fonts/slick.woff
new file mode 100644
index 0000000000..8ee99721bb
Binary files /dev/null and b/components/vc-slick/assets/fonts/slick.woff differ
diff --git a/components/vc-slick/assets/img/react-slick/abstract01.jpg b/components/vc-slick/assets/img/react-slick/abstract01.jpg
new file mode 100644
index 0000000000..4281849691
Binary files /dev/null and b/components/vc-slick/assets/img/react-slick/abstract01.jpg differ
diff --git a/components/vc-slick/assets/img/react-slick/abstract02.jpg b/components/vc-slick/assets/img/react-slick/abstract02.jpg
new file mode 100644
index 0000000000..d3ab983b79
Binary files /dev/null and b/components/vc-slick/assets/img/react-slick/abstract02.jpg differ
diff --git a/components/vc-slick/assets/img/react-slick/abstract03.jpg b/components/vc-slick/assets/img/react-slick/abstract03.jpg
new file mode 100644
index 0000000000..dfaa5a719a
Binary files /dev/null and b/components/vc-slick/assets/img/react-slick/abstract03.jpg differ
diff --git a/components/vc-slick/assets/img/react-slick/abstract04.jpg b/components/vc-slick/assets/img/react-slick/abstract04.jpg
new file mode 100644
index 0000000000..b98516dc7e
Binary files /dev/null and b/components/vc-slick/assets/img/react-slick/abstract04.jpg differ
diff --git a/components/vc-slick/assets/index.less b/components/vc-slick/assets/index.less
new file mode 100644
index 0000000000..a17c4259e1
--- /dev/null
+++ b/components/vc-slick/assets/index.less
@@ -0,0 +1,3 @@
+@import "./slick";
+@import "./slick-theme";
+@import "./docs";
diff --git a/components/vc-slick/assets/slick-theme.less b/components/vc-slick/assets/slick-theme.less
new file mode 100644
index 0000000000..0a6dde6622
--- /dev/null
+++ b/components/vc-slick/assets/slick-theme.less
@@ -0,0 +1,204 @@
+@charset 'UTF-8';
+/* Slider */
+.slick-loading .slick-list
+{
+ background: #fff url('./ajax-loader.gif') center center no-repeat;
+}
+
+/* Icons */
+// @font-face
+// {
+// font-family: 'slick';
+// font-weight: normal;
+// font-style: normal;
+
+// src: url('./fonts/slick.eot');
+// src: url('./fonts/slick.eot?#iefix') format('embedded-opentype'), url('./fonts/slick.woff') format('woff'), url('./fonts/slick.ttf') format('truetype'), url('./fonts/slick.svg#slick') format('svg');
+// }
+/* Arrows */
+.slick-prev,
+.slick-next
+{
+ font-size: 0;
+ line-height: 0;
+
+ position: absolute;
+ top: 50%;
+
+ display: block;
+
+ width: 20px;
+ height: 20px;
+ padding: 0;
+ -webkit-transform: translate(0, -50%);
+ -ms-transform: translate(0, -50%);
+ transform: translate(0, -50%);
+
+ cursor: pointer;
+
+ color: transparent;
+ border: none;
+ outline: none;
+ background: transparent;
+}
+.slick-prev:hover,
+.slick-prev:focus,
+.slick-next:hover,
+.slick-next:focus
+{
+ color: transparent;
+ outline: none;
+ background: transparent;
+}
+.slick-prev:hover:before,
+.slick-prev:focus:before,
+.slick-next:hover:before,
+.slick-next:focus:before
+{
+ opacity: 1;
+}
+.slick-prev.slick-disabled:before,
+.slick-next.slick-disabled:before
+{
+ opacity: .25;
+}
+
+.slick-prev:before,
+.slick-next:before
+{
+ // font-family: 'slick';
+ font-size: 20px;
+ line-height: 1;
+
+ opacity: .75;
+ color: white;
+
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.slick-prev
+{
+ left: -25px;
+}
+[dir='rtl'] .slick-prev
+{
+ right: -25px;
+ left: auto;
+}
+.slick-prev:before
+{
+ content: '←';
+}
+[dir='rtl'] .slick-prev:before
+{
+ content: '→';
+}
+
+.slick-next
+{
+ right: -25px;
+}
+[dir='rtl'] .slick-next
+{
+ right: auto;
+ left: -25px;
+}
+.slick-next:before
+{
+ content: '→';
+}
+[dir='rtl'] .slick-next:before
+{
+ content: '←';
+}
+
+/* Dots */
+.slick-dotted.slick-slider
+{
+ margin-bottom: 30px;
+}
+
+.slick-dots
+{
+ position: absolute;
+ bottom: -25px;
+
+ display: block;
+
+ width: 100%;
+ padding: 0;
+ margin: 0;
+
+ list-style: none;
+
+ text-align: center;
+}
+.slick-dots li
+{
+ position: relative;
+
+ display: inline-block;
+
+ width: 20px;
+ height: 20px;
+ margin: 0 5px;
+ padding: 0;
+
+ cursor: pointer;
+}
+.slick-dots li button
+{
+ font-size: 0;
+ line-height: 0;
+
+ display: block;
+
+ width: 20px;
+ height: 20px;
+ padding: 5px;
+
+ cursor: pointer;
+
+ color: transparent;
+ border: 0;
+ outline: none;
+ background: transparent;
+}
+.slick-dots li button:hover,
+.slick-dots li button:focus
+{
+ outline: none;
+}
+.slick-dots li button:hover:before,
+.slick-dots li button:focus:before
+{
+ opacity: 1;
+}
+.slick-dots li button:before
+{
+ // font-family: 'slick';
+ font-size: 40px;
+ line-height: 20px;
+
+ position: absolute;
+ top: 0;
+ left: 0;
+
+ width: 20px;
+ height: 20px;
+
+ content: '•';
+ text-align: center;
+
+ opacity: .25;
+ color: black;
+
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+.slick-dots li.slick-active button:before
+{
+ opacity: .75;
+ color: black;
+}
diff --git a/components/vc-slick/assets/slick.less b/components/vc-slick/assets/slick.less
new file mode 100644
index 0000000000..57477e848b
--- /dev/null
+++ b/components/vc-slick/assets/slick.less
@@ -0,0 +1,119 @@
+/* Slider */
+.slick-slider
+{
+ position: relative;
+
+ display: block;
+ box-sizing: border-box;
+
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+
+ -webkit-touch-callout: none;
+ -khtml-user-select: none;
+ -ms-touch-action: pan-y;
+ touch-action: pan-y;
+ -webkit-tap-highlight-color: transparent;
+}
+
+.slick-list
+{
+ position: relative;
+
+ display: block;
+ overflow: hidden;
+
+ margin: 0;
+ padding: 0;
+}
+.slick-list:focus
+{
+ outline: none;
+}
+.slick-list.dragging
+{
+ cursor: pointer;
+ cursor: hand;
+}
+
+.slick-slider .slick-track,
+.slick-slider .slick-list
+{
+ -webkit-transform: translate3d(0, 0, 0);
+ -moz-transform: translate3d(0, 0, 0);
+ -ms-transform: translate3d(0, 0, 0);
+ -o-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0);
+}
+
+.slick-track
+{
+ position: relative;
+ top: 0;
+ left: 0;
+
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+.slick-track:before,
+.slick-track:after
+{
+ display: table;
+
+ content: '';
+}
+.slick-track:after
+{
+ clear: both;
+}
+.slick-loading .slick-track
+{
+ visibility: hidden;
+}
+
+.slick-slide
+{
+ display: none;
+ float: left;
+
+ height: 100%;
+ min-height: 1px;
+}
+[dir='rtl'] .slick-slide
+{
+ float: right;
+}
+.slick-slide img
+{
+ display: block;
+}
+.slick-slide.slick-loading img
+{
+ display: none;
+}
+.slick-slide.dragging img
+{
+ pointer-events: none;
+}
+.slick-initialized .slick-slide
+{
+ display: block;
+}
+.slick-loading .slick-slide
+{
+ visibility: hidden;
+}
+.slick-vertical .slick-slide
+{
+ display: block;
+
+ height: auto;
+
+ border: 1px solid transparent;
+}
+.slick-arrow.slick-hidden {
+ display: none;
+}
diff --git a/components/vc-slick/demo/AdaptiveHeight.jsx b/components/vc-slick/demo/AdaptiveHeight.jsx
new file mode 100644
index 0000000000..85d8abafde
--- /dev/null
+++ b/components/vc-slick/demo/AdaptiveHeight.jsx
@@ -0,0 +1,45 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ class: '',
+ props: {
+ dots: true,
+ infinite: true,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ adaptiveHeight: true,
+ },
+ }
+ return (
+
+
Adaptive height
+
+
+
1
+
+
+
+
3
+
See ....
+
Height is adaptive
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/AppendDots.jsx b/components/vc-slick/demo/AppendDots.jsx
new file mode 100644
index 0000000000..7994a57ebf
--- /dev/null
+++ b/components/vc-slick/demo/AppendDots.jsx
@@ -0,0 +1,69 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render (h) {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ },
+ scopedSlots: {
+ customPaging: ({ i }) => {
+ return (
+
+ {i + 1}
+
+ )
+ },
+ appendDots: ({ dots }) => {
+ return (
+
+ )
+ },
+ },
+ }
+ return (
+
+
Append Dots
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/AsNavFor.jsx b/components/vc-slick/demo/AsNavFor.jsx
new file mode 100644
index 0000000000..eeca1b97b3
--- /dev/null
+++ b/components/vc-slick/demo/AsNavFor.jsx
@@ -0,0 +1,75 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ data () {
+ return {
+ nav1: null,
+ nav2: null,
+ }
+ },
+
+ mounted () {
+ this.nav1 = this.$refs.slider1
+ this.nav2 = this.$refs.slider2
+ },
+
+ render () {
+ return (
+
+
Slider Syncing (AsNavFor)
+
First Slider
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
Second Slider
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/AutoPlay.jsx b/components/vc-slick/demo/AutoPlay.jsx
new file mode 100644
index 0000000000..5ddc6c151a
--- /dev/null
+++ b/components/vc-slick/demo/AutoPlay.jsx
@@ -0,0 +1,44 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ autoplay: true,
+ speed: 2000,
+ autoplaySpeed: 2000,
+ cssEase: 'linear',
+ },
+ }
+ return (
+
+
Auto Play
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/AutoPlayMethods.jsx b/components/vc-slick/demo/AutoPlayMethods.jsx
new file mode 100644
index 0000000000..d44b1138db
--- /dev/null
+++ b/components/vc-slick/demo/AutoPlayMethods.jsx
@@ -0,0 +1,59 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ methods: {
+ play () {
+ this.$refs.slider.slickPlay()
+ },
+ pause () {
+ this.$refs.slider.slickPause()
+ },
+ },
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ autoplay: true,
+ autoplaySpeed: 2000,
+ },
+ ref: 'slider',
+ }
+ return (
+
+
Auto Play & Pause with buttons
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/CenterMode.jsx b/components/vc-slick/demo/CenterMode.jsx
new file mode 100644
index 0000000000..dd762a8a13
--- /dev/null
+++ b/components/vc-slick/demo/CenterMode.jsx
@@ -0,0 +1,42 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ centerMode: true,
+ infinite: true,
+ centerPadding: '60px',
+ slidesToShow: 3,
+ speed: 500,
+ },
+ class: 'center',
+ }
+ return (
+
+
Center Mode
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/CustomArrows.jsx b/components/vc-slick/demo/CustomArrows.jsx
new file mode 100644
index 0000000000..95c224d335
--- /dev/null
+++ b/components/vc-slick/demo/CustomArrows.jsx
@@ -0,0 +1,62 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render (h) {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ },
+ scopedSlots: {
+ nextArrow: (props) => {
+ const { class: className, style, on: { click }} = props
+ return (
+
+ )
+ },
+ prevArrow: (props) => {
+ const { class: className, style, on: { click }} = props
+ return (
+
+ )
+ },
+ },
+ }
+ return (
+
+
Custom Arrows
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/CustomPaging.jsx b/components/vc-slick/demo/CustomPaging.jsx
new file mode 100644
index 0000000000..46861d078f
--- /dev/null
+++ b/components/vc-slick/demo/CustomPaging.jsx
@@ -0,0 +1,50 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+import imgList from './imglist'
+
+const {
+ abstract01, abstract02, abstract03, abstract04,
+} = imgList
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ dotsClass: 'slick-dots slick-thumb',
+ infinite: true,
+ speed: 500,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ },
+ scopedSlots: {
+ customPaging: ({ i }) => {
+ return (
+
+
+
+ )
+ },
+ },
+ }
+ return (
+
+
Custom Paging
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/CustomSlides.jsx b/components/vc-slick/demo/CustomSlides.jsx
new file mode 100644
index 0000000000..53773cba91
--- /dev/null
+++ b/components/vc-slick/demo/CustomSlides.jsx
@@ -0,0 +1,40 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+const CustomSlide = {
+ props: ['index'],
+ render () {
+ return (
+
+
{this.index}
+
+ )
+ },
+}
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ },
+ }
+ return (
+
+
Custom Slides
+
+
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/DynamicSlides.jsx b/components/vc-slick/demo/DynamicSlides.jsx
new file mode 100644
index 0000000000..951a3ec240
--- /dev/null
+++ b/components/vc-slick/demo/DynamicSlides.jsx
@@ -0,0 +1,43 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ data () {
+ return {
+ slides: [1, 2, 3, 4, 5, 6],
+ }
+ },
+ methods: {
+ click () {
+ this.slides = this.slides.length === 6 ? [1, 2, 3, 4, 5, 6, 7, 8, 9] : [1, 2, 3, 4, 5, 6]
+ },
+ },
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 3,
+ slidesToScroll: 3,
+ },
+ }
+ return (
+
+
Dynamic slides
+
+
+ {this.slides.map(function (slide) {
+ return (
+
+
{slide}
+
+ )
+ })}
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/Fade.jsx b/components/vc-slick/demo/Fade.jsx
new file mode 100644
index 0000000000..75d68f986d
--- /dev/null
+++ b/components/vc-slick/demo/Fade.jsx
@@ -0,0 +1,41 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+import imgList from './imglist'
+
+const {
+ abstract01, abstract02, abstract03, abstract04,
+} = imgList
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ fade: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ },
+ }
+ return (
+
+
Fade
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/FocusOnSelect.jsx b/components/vc-slick/demo/FocusOnSelect.jsx
new file mode 100644
index 0000000000..3001369afa
--- /dev/null
+++ b/components/vc-slick/demo/FocusOnSelect.jsx
@@ -0,0 +1,42 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ focusOnSelect: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ speed: 500,
+ },
+ }
+ return (
+
+
FocusOnSelect
+
Click on any slide to select and make it current slide
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/LazyLoad.jsx b/components/vc-slick/demo/LazyLoad.jsx
new file mode 100644
index 0000000000..d0483d4f00
--- /dev/null
+++ b/components/vc-slick/demo/LazyLoad.jsx
@@ -0,0 +1,42 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+import imgList from './imglist'
+
+const {
+ abstract01, abstract02, abstract03, abstract04,
+} = imgList
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ lazyLoad: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ initialSlide: 1,
+ },
+ }
+ return (
+
+
Lazy Load
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/MultipleItems.jsx b/components/vc-slick/demo/MultipleItems.jsx
new file mode 100644
index 0000000000..60c8a05138
--- /dev/null
+++ b/components/vc-slick/demo/MultipleItems.jsx
@@ -0,0 +1,50 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 3,
+ slidesToScroll: 3,
+ },
+ }
+ return (
+
+
Multiple items
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
7
+
+
+
8
+
+
+
9
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/MultipleRows.jsx b/components/vc-slick/demo/MultipleRows.jsx
new file mode 100644
index 0000000000..1ddb3ea54f
--- /dev/null
+++ b/components/vc-slick/demo/MultipleRows.jsx
@@ -0,0 +1,74 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ centerMode: true,
+ infinite: true,
+ centerPadding: '60px',
+ slidesToShow: 3,
+ speed: 500,
+ rows: 2,
+ slidesPerRow: 2,
+ },
+ class: 'center',
+ }
+ return (
+
+
Multiple Rows
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
7
+
+
+
8
+
+
+
9
+
+
+
10
+
+
+
11
+
+
+
12
+
+
+
13
+
+
+
14
+
+
+
15
+
+
+
16
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/PauseOnHover.jsx b/components/vc-slick/demo/PauseOnHover.jsx
new file mode 100644
index 0000000000..987a5ec465
--- /dev/null
+++ b/components/vc-slick/demo/PauseOnHover.jsx
@@ -0,0 +1,43 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ autoplay: true,
+ autoplaySpeed: 2000,
+ pauseOnHover: true,
+ },
+ }
+ return (
+
+
Pause On Hover
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/PreviousNextMethods.jsx b/components/vc-slick/demo/PreviousNextMethods.jsx
new file mode 100644
index 0000000000..24167436f6
--- /dev/null
+++ b/components/vc-slick/demo/PreviousNextMethods.jsx
@@ -0,0 +1,58 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ methods: {
+ next () {
+ this.$refs.slider.slickNext()
+ },
+ previous () {
+ this.$refs.slider.slickPrev()
+ },
+ },
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ },
+ ref: 'slider',
+ }
+ return (
+
+
Previous and Next methods
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/Resizable.jsx b/components/vc-slick/demo/Resizable.jsx
new file mode 100644
index 0000000000..95544149eb
--- /dev/null
+++ b/components/vc-slick/demo/Resizable.jsx
@@ -0,0 +1,87 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ data () {
+ return {
+ display: true,
+ width: 600,
+ }
+ },
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ },
+ }
+ return (
+
+
Resizable Collapsible
+
+
+
+
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/Responsive.jsx b/components/vc-slick/demo/Responsive.jsx
new file mode 100644
index 0000000000..102b65b0a2
--- /dev/null
+++ b/components/vc-slick/demo/Responsive.jsx
@@ -0,0 +1,74 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: false,
+ speed: 500,
+ slidesToShow: 4,
+ slidesToScroll: 4,
+ initialSlide: 0,
+ responsive: [
+ {
+ breakpoint: 1024,
+ settings: {
+ slidesToShow: 3,
+ slidesToScroll: 3,
+ infinite: true,
+ dots: true,
+ },
+ },
+ {
+ breakpoint: 600,
+ settings: {
+ slidesToShow: 2,
+ slidesToScroll: 2,
+ initialSlide: 2,
+ },
+ },
+ {
+ breakpoint: 480,
+ settings: {
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ },
+ },
+ ],
+ },
+ }
+ return (
+
+
Responsive
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
7
+
+
+
8
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/Rtl.jsx b/components/vc-slick/demo/Rtl.jsx
new file mode 100644
index 0000000000..83081568d3
--- /dev/null
+++ b/components/vc-slick/demo/Rtl.jsx
@@ -0,0 +1,43 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ autoplay: true,
+ autoplaySpeed: 2000,
+ rtl: true,
+ },
+ }
+ return (
+
+
Right to Left
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/SimpleSlider.jsx b/components/vc-slick/demo/SimpleSlider.jsx
new file mode 100644
index 0000000000..d659178198
--- /dev/null
+++ b/components/vc-slick/demo/SimpleSlider.jsx
@@ -0,0 +1,41 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ },
+ }
+ return (
+
+
Single Item
+
+ alert(e)}>
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/SlickGoTo.jsx b/components/vc-slick/demo/SlickGoTo.jsx
new file mode 100644
index 0000000000..cc328d84e8
--- /dev/null
+++ b/components/vc-slick/demo/SlickGoTo.jsx
@@ -0,0 +1,58 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+import imgList from './imglist'
+
+const {
+ abstract01, abstract02, abstract03, abstract04,
+} = imgList
+
+export default {
+ data () {
+ return {
+ slideIndex: 0,
+ updateCount: 0,
+ }
+ },
+
+ render () {
+ const settings = {
+ props: {
+ dots: false,
+ infinite: true,
+ speed: 500,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ afterChange: () => { this.updateCount = this.updateCount + 1 },
+ beforeChange: (current, next) => { this.slideIndex = next },
+ },
+ ref: 'slider',
+ }
+ return (
+
+
Slick Go To
+
Total updates: {this.updateCount}
+
this.$refs.slider.slickGoTo(e.target.value)}
+ value={this.slideIndex}
+ type='range'
+ min={0}
+ max={3}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/SlideChangeHooks.jsx b/components/vc-slick/demo/SlideChangeHooks.jsx
new file mode 100644
index 0000000000..1b77e43354
--- /dev/null
+++ b/components/vc-slick/demo/SlideChangeHooks.jsx
@@ -0,0 +1,55 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ data () {
+ return {
+ activeSlide: 0,
+ activeSlide2: 0,
+ }
+ },
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 1000,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ beforeChange: (current, next) => { this.activeSlide = next },
+ afterChange: current => { this.activeSlide2 = current },
+ },
+ }
+ return (
+
+
beforeChange and afterChange hooks
+
+ BeforeChange => activeSlide: {this.activeSlide}
+
+
+ AfterChange => activeSlide: {this.activeSlide2}
+
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/SwipeToSlide.jsx b/components/vc-slick/demo/SwipeToSlide.jsx
new file mode 100644
index 0000000000..fa9b5b732c
--- /dev/null
+++ b/components/vc-slick/demo/SwipeToSlide.jsx
@@ -0,0 +1,55 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ infinite: true,
+ centerPadding: '60px',
+ slidesToShow: 5,
+ swipeToSlide: true,
+ afterChange: function (index) {
+ console.log(
+ `Slider Changed to: ${index + 1}, background: #222; color: #bada55`
+ )
+ },
+ },
+ class: 'center',
+ }
+ return (
+
+
Swipe To Slide
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
7
+
+
+
8
+
+
+
9
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/UnevenSetsFinite.jsx b/components/vc-slick/demo/UnevenSetsFinite.jsx
new file mode 100644
index 0000000000..d249053574
--- /dev/null
+++ b/components/vc-slick/demo/UnevenSetsFinite.jsx
@@ -0,0 +1,41 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: false,
+ speed: 500,
+ slidesToScroll: 4,
+ slidesToShow: 4,
+ },
+ }
+ return (
+
+
Uneven sets (finite)
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/UnevenSetsInfinite.jsx b/components/vc-slick/demo/UnevenSetsInfinite.jsx
new file mode 100644
index 0000000000..bb5f466dcf
--- /dev/null
+++ b/components/vc-slick/demo/UnevenSetsInfinite.jsx
@@ -0,0 +1,41 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ speed: 500,
+ slidesToScroll: 4,
+ slidesToShow: 4,
+ },
+ }
+ return (
+
+
Uneven sets (infinite)
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/VariableWidth.jsx b/components/vc-slick/demo/VariableWidth.jsx
new file mode 100644
index 0000000000..c1a213d0e7
--- /dev/null
+++ b/components/vc-slick/demo/VariableWidth.jsx
@@ -0,0 +1,43 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ class: 'slider variable-width',
+ props: {
+ dots: true,
+ infinite: true,
+ centerMode: true,
+ slidesToShow: 1,
+ slidesToScroll: 1,
+ variableWidth: true,
+ },
+ }
+ return (
+
+
Variable width
+
+
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/VerticalMode.jsx b/components/vc-slick/demo/VerticalMode.jsx
new file mode 100644
index 0000000000..8bf855a543
--- /dev/null
+++ b/components/vc-slick/demo/VerticalMode.jsx
@@ -0,0 +1,48 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ vertical: true,
+ verticalSwiping: true,
+ beforeChange: function (currentSlide, nextSlide) {
+ console.log('before change', currentSlide, nextSlide)
+ },
+ afterChange: function (currentSlide) {
+ console.log('after change', currentSlide)
+ },
+ },
+ }
+ return (
+
+
Vertical Mode
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/VerticalSwipeToSlide.jsx b/components/vc-slick/demo/VerticalSwipeToSlide.jsx
new file mode 100644
index 0000000000..c7149d74a5
--- /dev/null
+++ b/components/vc-slick/demo/VerticalSwipeToSlide.jsx
@@ -0,0 +1,49 @@
+import '../assets/index.less'
+import Slider from '../src/slider'
+
+export default {
+ render () {
+ const settings = {
+ props: {
+ dots: true,
+ infinite: true,
+ slidesToShow: 3,
+ slidesToScroll: 1,
+ vertical: true,
+ verticalSwiping: true,
+ swipeToSlide: true,
+ beforeChange: function (currentSlide, nextSlide) {
+ console.log('before change', currentSlide, nextSlide)
+ },
+ afterChange: function (currentSlide) {
+ console.log('after change', currentSlide)
+ },
+ },
+ }
+ return (
+
+
Vertical Mode with Swipe To Slide
+
+
+
1
+
+
+
2
+
+
+
3
+
+
+
4
+
+
+
5
+
+
+
6
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/demo/config.js b/components/vc-slick/demo/config.js
new file mode 100644
index 0000000000..8245b7a958
--- /dev/null
+++ b/components/vc-slick/demo/config.js
@@ -0,0 +1,4 @@
+export const baseUrl =
+ process.env.NODE_ENV === 'production'
+ ? 'https://s3.amazonaws.com/static.neostack.com/img/react-slick'
+ : '/img/react-slick'
diff --git a/components/vc-slick/demo/imglist.js b/components/vc-slick/demo/imglist.js
new file mode 100644
index 0000000000..0341872f2c
--- /dev/null
+++ b/components/vc-slick/demo/imglist.js
@@ -0,0 +1,11 @@
+import abstract01 from '../assets/img/react-slick/abstract01.jpg'
+import abstract02 from '../assets/img/react-slick/abstract02.jpg'
+import abstract03 from '../assets/img/react-slick/abstract03.jpg'
+import abstract04 from '../assets/img/react-slick/abstract04.jpg'
+
+export default {
+ abstract01,
+ abstract02,
+ abstract03,
+ abstract04,
+}
diff --git a/components/vc-slick/demo/index.jsx b/components/vc-slick/demo/index.jsx
new file mode 100644
index 0000000000..86f2e9e01c
--- /dev/null
+++ b/components/vc-slick/demo/index.jsx
@@ -0,0 +1,67 @@
+import SimpleSlider from './SimpleSlider'
+import SlideChangeHooks from './SlideChangeHooks'
+import MultipleItems from './MultipleItems'
+import MultipleRows from './MultipleRows'
+import Responsive from './Responsive'
+import Resizable from './Resizable'
+import UnevenSetsInfinite from './UnevenSetsInfinite'
+import UnevenSetsFinite from './UnevenSetsFinite'
+import CenterMode from './CenterMode'
+import FocusOnSelect from './FocusOnSelect'
+import AutoPlay from './AutoPlay'
+import AutoPlayMethods from './AutoPlayMethods'
+import PauseOnHover from './PauseOnHover'
+import Rtl from './Rtl'
+import VariableWidth from './VariableWidth'
+import AdaptiveHeight from './AdaptiveHeight'
+import LazyLoad from './LazyLoad'
+import Fade from './Fade'
+import SlickGoTo from './SlickGoTo'
+import CustomArrows from './CustomArrows'
+import PreviousNextMethods from './PreviousNextMethods'
+import DynamicSlides from './DynamicSlides'
+import VerticalMode from './VerticalMode'
+import SwipeToSlide from './SwipeToSlide'
+import VerticalSwipeToSlide from './VerticalSwipeToSlide'
+import CustomPaging from './CustomPaging'
+import CustomSlides from './CustomSlides'
+import AsNavFor from './AsNavFor'
+import AppendDots from './AppendDots'
+
+export default {
+ render () {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ },
+}
diff --git a/components/vc-slick/src/arrows.js b/components/vc-slick/src/arrows.js
new file mode 100644
index 0000000000..d171ce8a87
--- /dev/null
+++ b/components/vc-slick/src/arrows.js
@@ -0,0 +1,129 @@
+import classnames from 'classnames'
+import { cloneElement } from '../../_util/vnode'
+import { canGoNext } from './utils/innerSliderUtils'
+
+function noop () {}
+
+export const PrevArrow = {
+ functional: true,
+ clickHandler (options, handle, e) {
+ if (e) {
+ e.preventDefault()
+ }
+ handle(options, e)
+ },
+ render (createElement, context) {
+ const { props } = context
+ const { clickHandler, infinite, currentSlide, slideCount, slidesToShow } = props
+ const prevClasses = { 'slick-arrow': true, 'slick-prev': true }
+ let prevHandler = function (e) {
+ if (e) {
+ e.preventDefault()
+ }
+ clickHandler({ message: 'previous' })
+ }
+
+ if (
+ !infinite && (currentSlide === 0 || slideCount <= slidesToShow)
+ ) {
+ prevClasses['slick-disabled'] = true
+ prevHandler = noop
+ }
+
+ const prevArrowProps = {
+ key: '0',
+ domProps: {
+ 'data-role': 'none',
+ },
+ class: classnames(prevClasses),
+ style: { display: 'block' },
+ on: {
+ click: prevHandler,
+ },
+ }
+ const customProps = {
+ currentSlide: currentSlide,
+ slideCount: slideCount,
+ }
+ let prevArrow
+
+ if (props.prevArrow) {
+ prevArrow = cloneElement(props.prevArrow({
+ ...prevArrowProps,
+ ...{
+ props: customProps,
+ },
+ }), {})
+ } else {
+ prevArrow = (
+
+ )
+ }
+
+ return prevArrow
+ },
+}
+
+export const NextArrow = {
+ functional: true,
+ clickHandler (options, handle, e) {
+ if (e) {
+ e.preventDefault()
+ }
+ handle(options, e)
+ },
+ render (createElement, context) {
+ const { props } = context
+ const { clickHandler, currentSlide, slideCount } = props
+
+ const nextClasses = { 'slick-arrow': true, 'slick-next': true }
+ let nextHandler = function (e) {
+ if (e) {
+ e.preventDefault()
+ }
+ clickHandler({ message: 'next' })
+ }
+ if (!canGoNext(props)) {
+ nextClasses['slick-disabled'] = true
+ nextHandler = noop
+ }
+
+ const nextArrowProps = {
+ key: '1',
+ domProps: {
+ 'data-role': 'none',
+ },
+ class: classnames(nextClasses),
+ style: { display: 'block' },
+ on: {
+ click: nextHandler,
+ },
+ }
+ const customProps = {
+ currentSlide: currentSlide,
+ slideCount: slideCount,
+ }
+ let nextArrow
+
+ if (props.nextArrow) {
+ nextArrow = cloneElement(props.nextArrow({
+ ...nextArrowProps,
+ ...{
+ props: customProps,
+ },
+ }), {})
+ } else {
+ nextArrow = (
+
+ )
+ }
+
+ return nextArrow
+ },
+}
diff --git a/components/vc-slick/src/default-props.js b/components/vc-slick/src/default-props.js
new file mode 100644
index 0000000000..6abc0720c1
--- /dev/null
+++ b/components/vc-slick/src/default-props.js
@@ -0,0 +1,69 @@
+import PropTypes from '../../_util/vue-types'
+
+const defaultProps = {
+ accessibility: PropTypes.bool.def(true),
+ // 自定义高度
+ adaptiveHeight: PropTypes.bool.def(false),
+ afterChange: PropTypes.any.def(null),
+ // appendDots: PropTypes.func.def((h, { dots }) => {
+ // return
+ // }),
+ arrows: PropTypes.bool.def(true),
+ autoplay: PropTypes.bool.def(false),
+ autoplaySpeed: PropTypes.number.def(3000),
+ beforeChange: PropTypes.any.def(null),
+ centerMode: PropTypes.bool.def(false),
+ centerPadding: PropTypes.string.def('50px'),
+ // className: '',
+ cssEase: PropTypes.string.def('ease'),
+ // customPaging: PropTypes.func.def((h, { i }) => {
+ // return
+ // }),
+ dots: PropTypes.bool.def(false),
+ dotsClass: PropTypes.string.def('slick-dots'),
+ draggable: PropTypes.bool.def(true),
+ unslick: PropTypes.bool.def(false),
+ easing: PropTypes.string.def('linear'),
+ edgeFriction: PropTypes.number.def(0.35),
+ fade: PropTypes.bool.def(false),
+ focusOnSelect: PropTypes.bool.def(false),
+ infinite: PropTypes.bool.def(true),
+ initialSlide: PropTypes.number.def(0),
+ lazyLoad: PropTypes.any.def(null),
+ // nextArrow: PropTypes.any.def(null),
+ verticalSwiping: PropTypes.bool.def(false),
+ asNavFor: PropTypes.any.def(null),
+ // onEdge: null,
+ // onInit: null,
+ // onLazyLoadError: null,
+ // onReInit: null,
+ // 圆点hover是否暂停
+ pauseOnDotsHover: PropTypes.bool.def(false),
+ // focus是否暂停
+ pauseOnFocus: PropTypes.bool.def(false),
+ // hover是否暂停
+ pauseOnHover: PropTypes.bool.def(true),
+ // prevArrow: PropTypes.any.def(null),
+ responsive: PropTypes.any.def(null),
+ rows: PropTypes.number.def(1),
+ rtl: PropTypes.bool.def(false),
+ slide: PropTypes.string.def('div'),
+ slidesPerRow: PropTypes.number.def(1),
+ slidesToScroll: PropTypes.number.def(1),
+ slidesToShow: PropTypes.number.def(1),
+ speed: PropTypes.number.def(500),
+ swipe: PropTypes.bool.def(true),
+ swipeEvent: PropTypes.any.def(null),
+ swipeToSlide: PropTypes.bool.def(false),
+ touchMove: PropTypes.bool.def(true),
+ touchThreshold: PropTypes.number.def(5),
+ useCSS: PropTypes.bool.def(true),
+ useTransform: PropTypes.bool.def(true),
+ variableWidth: PropTypes.bool.def(false),
+ vertical: PropTypes.bool.def(false),
+ waitForAnimate: PropTypes.bool.def(true),
+ children: PropTypes.array,
+ __propsSymbol__: PropTypes.any,
+}
+
+export default defaultProps
diff --git a/components/vc-slick/src/dots.js b/components/vc-slick/src/dots.js
new file mode 100644
index 0000000000..5ebbc7009a
--- /dev/null
+++ b/components/vc-slick/src/dots.js
@@ -0,0 +1,84 @@
+import classnames from 'classnames'
+import { cloneElement } from '../../_util/vnode'
+
+const getDotCount = function (spec) {
+ let dots
+
+ if (spec.infinite) {
+ dots = Math.ceil(spec.slideCount / spec.slidesToScroll)
+ } else {
+ dots =
+ Math.ceil((spec.slideCount - spec.slidesToShow) / spec.slidesToScroll) +
+ 1
+ }
+
+ return dots
+}
+
+export default {
+ functional: true,
+ render (createElement, context) {
+ const { props, listeners } = context
+ const {
+ slideCount, slidesToScroll, slidesToShow,
+ infinite, currentSlide, appendDots,
+ customPaging, clickHandler, dotsClass,
+ } = props
+ const dotCount = getDotCount({
+ slideCount: slideCount,
+ slidesToScroll: slidesToScroll,
+ slidesToShow: slidesToShow,
+ infinite: infinite,
+ })
+
+ // Apply join & split to Array to pre-fill it for IE8
+ //
+ // Credit: http://stackoverflow.com/a/13735425/1849458
+ const { mouseenter, mouseover, mouseleave } = listeners
+ const mouseEvents = { mouseenter, mouseover, mouseleave }
+ const dots = Array.apply(
+ null,
+ Array(dotCount + 1)
+ .join('0')
+ .split('')
+ ).map((x, i) => {
+ const leftBound = i * slidesToScroll
+ const rightBound =
+ i * slidesToScroll + (slidesToScroll - 1)
+ const className = classnames({
+ 'slick-active':
+ currentSlide >= leftBound &&
+ currentSlide <= rightBound,
+ })
+
+ const dotOptions = {
+ message: 'dots',
+ index: i,
+ slidesToScroll: slidesToScroll,
+ currentSlide: currentSlide,
+ }
+ function onClick (e) {
+ // In Autoplay the focus stays on clicked button even after transition
+ // to next slide. That only goes away by click somewhere outside
+ if (e) {
+ e.preventDefault()
+ }
+ clickHandler(dotOptions)
+ }
+ return (
+
+ {cloneElement(customPaging({ i }), { on: {
+ click: onClick,
+ }})}
+
+ )
+ })
+
+ return cloneElement(appendDots({ dots }), {
+ class: dotsClass,
+ on: {
+ ...mouseEvents,
+ },
+ })
+ },
+}
diff --git a/components/vc-slick/src/index.js b/components/vc-slick/src/index.js
new file mode 100644
index 0000000000..712f4192f4
--- /dev/null
+++ b/components/vc-slick/src/index.js
@@ -0,0 +1,3 @@
+import Slider from './slider'
+
+export default Slider
diff --git a/components/vc-slick/src/initial-state.js b/components/vc-slick/src/initial-state.js
new file mode 100644
index 0000000000..434da0eefb
--- /dev/null
+++ b/components/vc-slick/src/initial-state.js
@@ -0,0 +1,26 @@
+const initialState = {
+ animating: false,
+ autoplaying: null,
+ currentDirection: 0,
+ currentLeft: null,
+ currentSlide: 0,
+ direction: 1,
+ dragging: false,
+ edgeDragged: false,
+ initialized: false,
+ lazyLoadedList: [],
+ listHeight: null,
+ listWidth: null,
+ scrolling: false,
+ slideCount: null,
+ slideHeight: null,
+ slideWidth: null,
+ swipeLeft: null,
+ swiped: false, // used by swipeEvent. differentites between touch and swipe.
+ swiping: false,
+ touchObject: { startX: 0, startY: 0, curX: 0, curY: 0 },
+ trackStyle: {},
+ trackWidth: 0,
+}
+
+export default initialState
diff --git a/components/vc-slick/src/inner-slider.js b/components/vc-slick/src/inner-slider.js
new file mode 100644
index 0000000000..b052adaba6
--- /dev/null
+++ b/components/vc-slick/src/inner-slider.js
@@ -0,0 +1,851 @@
+import debounce from 'lodash/debounce'
+import classnames from 'classnames'
+import Vue from 'vue'
+import antRefDirective from '../../_util/antRefDirective'
+import { getStyle } from '../../_util/props-util'
+import BaseMixin from '../../_util/BaseMixin'
+import defaultProps from './default-props'
+import initialState from './initial-state'
+import {
+ getOnDemandLazySlides,
+ extractObject,
+ initializedState,
+ getHeight,
+ canGoNext,
+ slideHandler,
+ changeSlide,
+ keyHandler,
+ swipeStart,
+ swipeMove,
+ swipeEnd,
+ getPreClones,
+ getPostClones,
+ getTrackLeft,
+ getTrackCSS,
+} from './utils/innerSliderUtils'
+import Track from './track'
+import Dots from './dots'
+import { PrevArrow, NextArrow } from './arrows'
+import ResizeObserver from 'resize-observer-polyfill'
+
+Vue.use(antRefDirective)
+
+function noop () {}
+
+export default {
+ props: {
+ ...defaultProps,
+ },
+ mixins: [BaseMixin],
+ data () {
+ this.preProps = { ...this.$props }
+ this.list = null
+ this.track = null
+ this.callbackTimers = []
+ this.clickable = true
+ this.debouncedResize = null
+ return {
+ ...initialState,
+ currentSlide: this.initialSlide,
+ slideCount: this.children.length,
+ }
+ },
+ methods: {
+ listRefHandler (ref) {
+ this.list = ref && ref.elm
+ },
+ trackRefHandler (ref) {
+ this.track = ref && ref.elm
+ },
+ adaptHeight () {
+ if (this.adaptiveHeight && this.list) {
+ const elem = this.list.querySelector(
+ `[data-index="${this.currentSlide}"]`
+ )
+ this.list.style.height = getHeight(elem) + 'px'
+ }
+ },
+ onWindowResized (setTrackStyle) {
+ if (this.debouncedResize) this.debouncedResize.cancel()
+ this.debouncedResize = debounce(() => this.resizeWindow(setTrackStyle), 50)
+ this.debouncedResize()
+ },
+ resizeWindow (setTrackStyle = true) {
+ if (!this.track) return
+ const spec = {
+ listRef: this.list,
+ trackRef: this.track,
+ children: this.children,
+ ...this.$props,
+ ...this.$data,
+ }
+ this.updateState(spec, setTrackStyle, () => {
+ if (this.autoplay) {
+ this.handleAutoPlay('update')
+ } else {
+ this.pause('paused')
+ }
+ })
+ // animating state should be cleared while resizing, otherwise autoplay stops working
+ this.setState({
+ animating: false,
+ })
+ clearTimeout(this.animationEndCallback)
+ delete this.animationEndCallback
+ },
+ updateState (spec, setTrackStyle, callback) {
+ const updatedState = initializedState(spec)
+ spec = { ...spec, ...updatedState, slideIndex: updatedState.currentSlide }
+ const targetLeft = getTrackLeft(spec)
+ spec = { ...spec, left: targetLeft }
+ const trackStyle = getTrackCSS(spec)
+ if (
+ setTrackStyle ||
+ this.children.length !==
+ spec.children.length
+ ) {
+ updatedState['trackStyle'] = trackStyle
+ }
+ this.setState(updatedState, callback)
+ },
+ ssrInit () {
+ const children = this.children
+ if (this.variableWidth) {
+ let trackWidth = 0
+ let trackLeft = 0
+ const childrenWidths = []
+ const preClones = getPreClones({
+ ...this.$props,
+ ...this.$data,
+ slideCount: children.length,
+ })
+ const postClones = getPostClones({
+ ...this.$props,
+ ...this.$data,
+ slideCount: children.length,
+ })
+ children.forEach(child => {
+ const childWidth = getStyle(child).width.split('px')[0]
+ childrenWidths.push(childWidth)
+ trackWidth += childWidth
+ })
+ for (let i = 0; i < preClones; i++) {
+ trackLeft += childrenWidths[childrenWidths.length - 1 - i]
+ trackWidth += childrenWidths[childrenWidths.length - 1 - i]
+ }
+ for (let i = 0; i < postClones; i++) {
+ trackWidth += childrenWidths[i]
+ }
+ for (let i = 0; i < this.currentSlide; i++) {
+ trackLeft += childrenWidths[i]
+ }
+ const trackStyle = {
+ width: trackWidth + 'px',
+ left: -trackLeft + 'px',
+ }
+ if (this.centerMode) {
+ const currentWidth = `${childrenWidths[this.currentSlide]}px`
+ trackStyle.left = `calc(${
+ trackStyle.left
+ } + (100% - ${currentWidth}) / 2 ) `
+ }
+ this.setState({
+ trackStyle,
+ })
+ return
+ }
+ const childrenCount = children.length
+ const spec = { ...this.$props, ...this.$data, slideCount: childrenCount }
+ const slideCount = getPreClones(spec) + getPostClones(spec) + childrenCount
+ const trackWidth = 100 / this.slidesToShow * slideCount
+ const slideWidth = 100 / slideCount
+ let trackLeft =
+ -slideWidth *
+ (getPreClones(spec) + this.currentSlide) *
+ trackWidth /
+ 100
+ if (this.centerMode) {
+ trackLeft += (100 - slideWidth * trackWidth / 100) / 2
+ }
+ const trackStyle = {
+ width: trackWidth + '%',
+ left: trackLeft + '%',
+ }
+ this.setState({
+ slideWidth: slideWidth + '%',
+ trackStyle: trackStyle,
+ })
+ },
+ checkImagesLoad () {
+ const images = document.querySelectorAll('.slick-slide img')
+ const imagesCount = images.length
+ let loadedCount = 0
+ Array.prototype.forEach.call(images, image => {
+ const handler = () =>
+ ++loadedCount && loadedCount >= imagesCount && this.onWindowResized()
+ if (!image.onclick) {
+ image.onclick = () => image.parentNode.focus()
+ } else {
+ const prevClickHandler = image.onclick
+ image.onclick = () => {
+ prevClickHandler()
+ image.parentNode.focus()
+ }
+ }
+ if (!image.onload) {
+ if (this.$props.lazyLoad) {
+ image.onload = () => {
+ this.adaptHeight()
+ this.callbackTimers.push(
+ setTimeout(this.onWindowResized, this.speed)
+ )
+ }
+ } else {
+ image.onload = handler
+ image.onerror = () => {
+ handler()
+ this.$emit('lazyLoadError')
+ }
+ }
+ }
+ })
+ },
+ progressiveLazyLoad () {
+ const slidesToLoad = []
+ const spec = { ...this.$props, ...this.$data }
+ for (
+ let index = this.currentSlide;
+ index < this.slideCount + getPostClones(spec);
+ index++
+ ) {
+ if (this.lazyLoadedList.indexOf(index) < 0) {
+ slidesToLoad.push(index)
+ break
+ }
+ }
+ for (
+ let index = this.currentSlide - 1;
+ index >= -getPreClones(spec);
+ index--
+ ) {
+ if (this.lazyLoadedList.indexOf(index) < 0) {
+ slidesToLoad.push(index)
+ break
+ }
+ }
+ if (slidesToLoad.length > 0) {
+ this.setState(state => ({
+ lazyLoadedList: state.lazyLoadedList.concat(slidesToLoad),
+ }))
+ this.$emit('lazyLoad', slidesToLoad)
+ } else {
+ if (this.lazyLoadTimer) {
+ clearInterval(this.lazyLoadTimer)
+ delete this.lazyLoadTimer
+ }
+ }
+ },
+ slideHandler (index, dontAnimate = false) {
+ const {
+ asNavFor,
+ currentSlide,
+ beforeChange,
+ speed,
+ afterChange,
+ } = this.$props
+ const { state, nextState } = slideHandler({
+ index,
+ ...this.$props,
+ ...this.$data,
+ trackRef: this.track,
+ useCSS: this.useCSS && !dontAnimate,
+ })
+ if (!state) return
+ beforeChange && beforeChange(currentSlide, state.currentSlide)
+ const slidesToLoad = state.lazyLoadedList.filter(
+ value => this.lazyLoadedList.indexOf(value) < 0
+ )
+ if (this.$listeners.lazyLoad && slidesToLoad.length > 0) {
+ this.$emit('lazyLoad', slidesToLoad)
+ }
+ this.setState(state, () => {
+ asNavFor &&
+ asNavFor.innerSlider.currentSlide !== currentSlide &&
+ asNavFor.innerSlider.slideHandler(index)
+ if (!nextState) return
+ this.animationEndCallback = setTimeout(() => {
+ const { animating, ...firstBatch } = nextState
+ this.setState(firstBatch, () => {
+ this.callbackTimers.push(
+ setTimeout(() => this.setState({ animating }), 10)
+ )
+ afterChange && afterChange(state.currentSlide)
+ delete this.animationEndCallback
+ })
+ }, speed)
+ })
+ },
+ changeSlide (options, dontAnimate = false) {
+ const spec = { ...this.$props, ...this.$data }
+ const targetSlide = changeSlide(spec, options)
+ if (targetSlide !== 0 && !targetSlide) return
+ if (dontAnimate === true) {
+ this.slideHandler(targetSlide, dontAnimate)
+ } else {
+ this.slideHandler(targetSlide)
+ }
+ },
+ clickHandler (e) {
+ if (this.clickable === false) {
+ e.stopPropagation()
+ e.preventDefault()
+ }
+ this.clickable = true
+ },
+ keyHandler (e) {
+ const dir = keyHandler(e, this.accessibility, this.rtl)
+ dir !== '' && this.changeSlide({ message: dir })
+ },
+ selectHandler (options) {
+ this.changeSlide(options)
+ },
+ disableBodyScroll () {
+ const preventDefault = e => {
+ e = e || window.event
+ if (e.preventDefault) e.preventDefault()
+ e.returnValue = false
+ }
+ window.ontouchmove = preventDefault
+ },
+ enableBodyScroll () {
+ window.ontouchmove = null
+ },
+ swipeStart (e) {
+ if (this.verticalSwiping) {
+ this.disableBodyScroll()
+ }
+ const state = swipeStart(e, this.swipe, this.draggable)
+ state !== '' && this.setState(state)
+ },
+ swipeMove (e) {
+ const state = swipeMove(e, {
+ ...this.$props,
+ ...this.$data,
+ trackRef: this.track,
+ listRef: this.list,
+ slideIndex: this.currentSlide,
+ })
+ if (!state) return
+ if (state['swiping']) {
+ this.clickable = false
+ }
+ this.setState(state)
+ },
+ swipeEnd (e) {
+ const state = swipeEnd(e, {
+ ...this.$props,
+ ...this.$data,
+ trackRef: this.track,
+ listRef: this.list,
+ slideIndex: this.currentSlide,
+ })
+ if (!state) return
+ const triggerSlideHandler = state['triggerSlideHandler']
+ delete state['triggerSlideHandler']
+ this.setState(state)
+ if (triggerSlideHandler === undefined) return
+ this.slideHandler(triggerSlideHandler)
+ if (this.$props.verticalSwiping) {
+ this.enableBodyScroll()
+ }
+ },
+ slickPrev () {
+ // this and fellow methods are wrapped in setTimeout
+ // to make sure initialize setState has happened before
+ // any of such methods are called
+ this.callbackTimers.push(
+ setTimeout(() => this.changeSlide({ message: 'previous' }), 0)
+ )
+ },
+ slickNext () {
+ this.callbackTimers.push(
+ setTimeout(() => this.changeSlide({ message: 'next' }), 0)
+ )
+ },
+ slickGoTo (slide, dontAnimate = false) {
+ slide = Number(slide)
+ if (isNaN(slide)) return ''
+ this.callbackTimers.push(
+ setTimeout(
+ () =>
+ this.changeSlide(
+ {
+ message: 'index',
+ index: slide,
+ currentSlide: this.currentSlide,
+ },
+ dontAnimate
+ ),
+ 0
+ )
+ )
+ },
+ play () {
+ let nextIndex
+ if (this.rtl) {
+ nextIndex = this.currentSlide - this.slidesToScroll
+ } else {
+ if (canGoNext({ ...this.$props, ...this.$data })) {
+ nextIndex = this.currentSlide + this.slidesToScroll
+ } else {
+ return false
+ }
+ }
+
+ this.slideHandler(nextIndex)
+ },
+ handleAutoPlay (playType) {
+ if (this.autoplayTimer) {
+ clearInterval(this.autoplayTimer)
+ }
+ const autoplaying = this.autoplaying
+ if (playType === 'update') {
+ if (
+ autoplaying === 'hovered' ||
+ autoplaying === 'focused' ||
+ autoplaying === 'paused'
+ ) {
+ return
+ }
+ } else if (playType === 'leave') {
+ if (autoplaying === 'paused' || autoplaying === 'focused') {
+ return
+ }
+ } else if (playType === 'blur') {
+ if (autoplaying === 'paused' || autoplaying === 'hovered') {
+ return
+ }
+ }
+ this.autoplayTimer = setInterval(this.play, this.autoplaySpeed + 50)
+ this.setState({ autoplaying: 'playing' })
+ },
+ pause (pauseType) {
+ if (this.autoplayTimer) {
+ clearInterval(this.autoplayTimer)
+ this.autoplayTimer = null
+ }
+ const autoplaying = this.autoplaying
+ if (pauseType === 'paused') {
+ this.setState({ autoplaying: 'paused' })
+ } else if (pauseType === 'focused') {
+ if (autoplaying === 'hovered' || autoplaying === 'playing') {
+ this.setState({ autoplaying: 'focused' })
+ }
+ } else {
+ // pauseType is 'hovered'
+ if (autoplaying === 'playing') {
+ this.setState({ autoplaying: 'hovered' })
+ }
+ }
+ },
+ onDotsOver () {
+ this.autoplay && this.pause('hovered')
+ },
+ onDotsLeave () {
+ this.autoplay &&
+ this.autoplaying === 'hovered' &&
+ this.handleAutoPlay('leave')
+ },
+ onTrackOver () {
+ this.autoplay && this.pause('hovered')
+ },
+ onTrackLeave () {
+ this.autoplay &&
+ this.autoplaying === 'hovered' &&
+ this.handleAutoPlay('leave')
+ },
+ onSlideFocus () {
+ this.autoplay && this.pause('focused')
+ },
+ onSlideBlur () {
+ this.autoplay &&
+ this.autoplaying === 'focused' &&
+ this.handleAutoPlay('blur')
+ },
+ customPaging ({ i }) {
+ return
+ },
+ appendDots ({ dots }) {
+ return
+ },
+ },
+ beforeMount () {
+ this.ssrInit()
+ this.$emit('init')
+ if (this.lazyLoad) {
+ const slidesToLoad = getOnDemandLazySlides({
+ ...this.$props,
+ ...this.$data,
+ })
+ if (slidesToLoad.length > 0) {
+ this.setState(prevState => ({
+ lazyLoadedList: prevState.lazyLoadedList.concat(slidesToLoad),
+ }))
+ this.$emit('lazyLoad', slidesToLoad)
+ }
+ }
+ },
+ mounted () {
+ this.$nextTick(() => {
+ const spec = {
+ listRef: this.list,
+ trackRef: this.track,
+ children: this.children,
+ ...this.$props,
+ }
+ this.updateState(spec, true, () => {
+ this.adaptHeight()
+ this.autoplay && this.handleAutoPlay('update')
+ })
+ if (this.lazyLoad === 'progressive') {
+ this.lazyLoadTimer = setInterval(this.progressiveLazyLoad, 1000)
+ }
+ this.ro = new ResizeObserver(() => {
+ if (this.animating) {
+ this.onWindowResized(false) // don't set trackStyle hence don't break animation
+ this.callbackTimers.push(
+ setTimeout(() => this.onWindowResized(), this.speed)
+ )
+ } else {
+ this.onWindowResized()
+ }
+ })
+ this.ro.observe(this.list)
+ Array.prototype.forEach.call(
+ document.querySelectorAll('.slick-slide'),
+ slide => {
+ slide.onfocus = this.$props.pauseOnFocus ? this.onSlideFocus : null
+ slide.onblur = this.$props.pauseOnFocus ? this.onSlideBlur : null
+ }
+ )
+ // To support server-side rendering
+ if (!window) {
+ return
+ }
+ if (window.addEventListener) {
+ window.addEventListener('resize', this.onWindowResized)
+ } else {
+ window.attachEvent('onresize', this.onWindowResized)
+ }
+ })
+ },
+ beforeDestroy () {
+ if (this.animationEndCallback) {
+ clearTimeout(this.animationEndCallback)
+ }
+ if (this.lazyLoadTimer) {
+ clearInterval(this.lazyLoadTimer)
+ }
+ if (this.callbackTimers.length) {
+ this.callbackTimers.forEach(timer => clearTimeout(timer))
+ this.callbackTimers = []
+ }
+ if (window.addEventListener) {
+ window.removeEventListener('resize', this.onWindowResized)
+ } else {
+ window.detachEvent('onresize', this.onWindowResized)
+ }
+ if (this.autoplayTimer) {
+ clearInterval(this.autoplayTimer)
+ }
+ },
+ updated () {
+ this.checkImagesLoad()
+ this.$emit('reInit')
+ if (this.lazyLoad) {
+ const slidesToLoad = getOnDemandLazySlides({
+ ...this.$props,
+ ...this.$data,
+ })
+ if (slidesToLoad.length > 0) {
+ this.setState(prevState => ({
+ lazyLoadedList: prevState.lazyLoadedList.concat(slidesToLoad),
+ }))
+ this.$emit('lazyLoad')
+ }
+ }
+ // if (this.props.onLazyLoad) {
+ // this.props.onLazyLoad([leftMostSlide])
+ // }
+ this.adaptHeight()
+ },
+ watch: {
+ __propsSymbol__ () {
+ const nextProps = this.$props
+ const spec = {
+ listRef: this.list,
+ trackRef: this.track,
+ ...nextProps,
+ ...this.$data,
+ }
+ let setTrackStyle = false
+ for (const key of Object.keys(this.preProps)) {
+ if (!nextProps.hasOwnProperty(key)) {
+ setTrackStyle = true
+ break
+ }
+ if (
+ typeof nextProps[key] === 'object' ||
+ typeof nextProps[key] === 'function' ||
+ typeof nextProps[key] === 'symbol'
+ ) {
+ continue
+ }
+ if (nextProps[key] !== this.preProps[key]) {
+ setTrackStyle = true
+ break
+ }
+ }
+ this.updateState(spec, setTrackStyle, () => {
+ if (this.currentSlide >= nextProps.children.length) {
+ this.changeSlide({
+ message: 'index',
+ index: nextProps.children.length - nextProps.slidesToShow,
+ currentSlide: this.currentSlide,
+ })
+ }
+ if (nextProps.autoplay) {
+ this.handleAutoPlay('update')
+ } else {
+ this.pause('paused')
+ }
+ })
+ this.preProps = { ...nextProps }
+ },
+ // '$props': function (props) {
+ // const spec = {
+ // listRef: this.list,
+ // trackRef: this.track,
+ // children: this.$slots.default,
+ // ...props,
+ // ...this.$data,
+ // }
+ // let setTrackStyle = false
+ // for (const key of Object.keys(this.$props)) {
+ // if (!props.hasOwnProperty(key)) {
+ // setTrackStyle = true
+ // break
+ // }
+ // if (
+ // typeof props[key] === 'object' ||
+ // typeof props[key] === 'function'
+ // ) {
+ // continue
+ // }
+ // if (props[key] !== this.$props[key]) {
+ // setTrackStyle = true
+ // break
+ // }
+ // }
+ // this.updateState(spec, setTrackStyle, () => {
+ // const children = this.$slots.default
+ // if (this.currentSlide >= children.length) {
+ // this.changeSlide({
+ // message: 'index',
+ // index:
+ // children.length - props.slidesToShow,
+ // currentSlide: this.currentSlide,
+ // })
+ // }
+ // if (props.autoplay) {
+ // this.handleAutoPlay('update')
+ // } else {
+ // this.pause('paused')
+ // }
+ // })
+ // },
+ },
+ render () {
+ const className = classnames('slick-slider', {
+ 'slick-vertical': this.vertical,
+ 'slick-initialized': true,
+ })
+ const spec = { ...this.$props, ...this.$data }
+ let trackProps = extractObject(spec, [
+ 'fade',
+ 'cssEase',
+ 'speed',
+ 'infinite',
+ 'centerMode',
+ 'focusOnSelect',
+ 'currentSlide',
+ 'lazyLoad',
+ 'lazyLoadedList',
+ 'rtl',
+ 'slideWidth',
+ 'slideHeight',
+ 'listHeight',
+ 'vertical',
+ 'slidesToShow',
+ 'slidesToScroll',
+ 'slideCount',
+ 'trackStyle',
+ 'variableWidth',
+ 'unslick',
+ 'centerPadding',
+ ])
+ const { pauseOnHover } = this.$props
+ trackProps = {
+ props: {
+ ...trackProps,
+ focusOnSelect: this.focusOnSelect ? this.selectHandler : null,
+ },
+ directives: [{
+ name: 'ant-ref',
+ value: this.trackRefHandler,
+ }],
+ on: {
+ mouseenter: pauseOnHover ? this.onTrackOver : noop,
+ mouseleave: pauseOnHover ? this.onTrackLeave : noop,
+ mouseover: pauseOnHover ? this.onTrackOver : noop,
+ },
+ }
+
+ let dots
+ if (
+ this.dots === true &&
+ this.slideCount >= this.slidesToShow
+ ) {
+ let dotProps = extractObject(spec, [
+ 'dotsClass',
+ 'slideCount',
+ 'slidesToShow',
+ 'currentSlide',
+ 'slidesToScroll',
+ 'clickHandler',
+ 'children',
+ 'infinite',
+ 'appendDots',
+ ])
+ dotProps.customPaging = this.customPaging
+ dotProps.appendDots = this.appendDots
+ const { customPaging, appendDots } = this.$scopedSlots
+ if (customPaging) {
+ dotProps.customPaging = customPaging
+ }
+ if (appendDots) {
+ dotProps.appendDots = appendDots
+ }
+ const { pauseOnDotsHover } = this.$props
+ dotProps = {
+ props: {
+ ...dotProps,
+ clickHandler: this.changeSlide,
+ },
+ on: {
+ mouseenter: pauseOnDotsHover ? this.onDotsLeave : noop,
+ mouseover: pauseOnDotsHover ? this.onDotsOver : noop,
+ mouseleave: pauseOnDotsHover ? this.onDotsLeave : noop,
+ },
+ }
+ dots =
+ }
+
+ let prevArrow, nextArrow
+ const arrowProps = extractObject(spec, [
+ 'infinite',
+ 'centerMode',
+ 'currentSlide',
+ 'slideCount',
+ 'slidesToShow',
+ ])
+ arrowProps.clickHandler = this.changeSlide
+ const { prevArrow: prevArrowCustom, nextArrow: nextArrowCustom } = this.$scopedSlots
+ if (prevArrowCustom) {
+ arrowProps.prevArrow = prevArrowCustom
+ }
+ if (nextArrowCustom) {
+ arrowProps.nextArrow = nextArrowCustom
+ }
+ if (this.arrows) {
+ prevArrow =
+ nextArrow =
+ }
+ let verticalHeightStyle = null
+
+ if (this.vertical) {
+ verticalHeightStyle = {
+ height: typeof this.listHeight === 'number' ? `${this.listHeight}px` : this.listHeight,
+ }
+ }
+
+ let centerPaddingStyle = null
+
+ if (this.vertical === false) {
+ if (this.centerMode === true) {
+ centerPaddingStyle = {
+ padding: '0px ' + this.centerPadding,
+ }
+ }
+ } else {
+ if (this.centerMode === true) {
+ centerPaddingStyle = {
+ padding: this.centerPadding + ' 0px',
+ }
+ }
+ }
+
+ const listStyle = { ...verticalHeightStyle, ...centerPaddingStyle }
+ const touchMove = this.touchMove
+ let listProps = {
+ directives: [{
+ name: 'ant-ref',
+ value: this.listRefHandler,
+ }],
+ class: 'slick-list',
+ style: listStyle,
+ on: {
+ click: this.clickHandler,
+ mousedown: touchMove ? this.swipeStart : noop,
+ mousemove: this.dragging && touchMove ? this.swipeMove : noop,
+ mouseup: touchMove ? this.swipeEnd : noop,
+ mouseleave: this.dragging && touchMove ? this.swipeEnd : noop,
+ touchstart: touchMove ? this.swipeStart : noop,
+ touchmove: this.dragging && touchMove ? this.swipeMove : noop,
+ touchend: touchMove ? this.swipeEnd : noop,
+ touchcancel: this.dragging && touchMove ? this.swipeEnd : noop,
+ keydown: this.accessibility ? this.keyHandler : noop,
+ },
+ }
+
+ let innerSliderProps = {
+ class: className,
+ props: {
+ dir: 'ltr',
+ },
+ }
+
+ if (this.unslick) {
+ listProps = {
+ class: 'slick-list',
+ directives: [{
+ name: 'ant-ref',
+ value: this.listRefHandler,
+ }],
+ }
+ innerSliderProps = { class: className }
+ }
+ return (
+
+ {!this.unslick ? prevArrow : ''}
+
+
+
+ {!this.unslick ? nextArrow : ''}
+ {!this.unslick ? dots : ''}
+
+ )
+ },
+}
diff --git a/components/vc-slick/src/slider.js b/components/vc-slick/src/slider.js
new file mode 100644
index 0000000000..8014d63ae1
--- /dev/null
+++ b/components/vc-slick/src/slider.js
@@ -0,0 +1,241 @@
+import json2mq from 'json2mq'
+import Vue from 'vue'
+import antRefDirective from '../../_util/antRefDirective'
+import BaseMixin from '../../_util/BaseMixin'
+import { cloneElement } from '../../_util/vnode'
+import { getStyle } from '../../_util/props-util'
+import InnerSlider from './inner-slider'
+import defaultProps from './default-props'
+import { canUseDOM } from './utils/innerSliderUtils'
+const enquire = canUseDOM() && require('enquire.js')
+
+Vue.use(antRefDirective)
+export default {
+ props: {
+ ...defaultProps,
+ },
+ mixins: [BaseMixin],
+ data () {
+ this._responsiveMediaHandlers = []
+ return {
+ breakpoint: null,
+ }
+ },
+ methods: {
+ innerSliderRefHandler (ref) {
+ this.innerSlider = ref && ref.componentInstance
+ },
+ media (query, handler) {
+ // javascript handler for css media query
+ enquire.register(query, handler)
+ this._responsiveMediaHandlers.push({ query, handler })
+ },
+ slickPrev () {
+ this.innerSlider.slickPrev()
+ },
+ slickNext () {
+ this.innerSlider.slickNext()
+ },
+ slickGoTo (slide, dontAnimate = false) {
+ this.innerSlider.slickGoTo(slide, dontAnimate)
+ },
+ slickPause () {
+ this.innerSlider.pause('paused')
+ },
+ slickPlay () {
+ this.innerSlider.handleAutoPlay('play')
+ },
+ },
+ // handles responsive breakpoints
+ beforeMount () {
+ // performance monitoring
+ // if (process.env.NODE_ENV !== 'production') {
+ // const { whyDidYouUpdate } = require('why-did-you-update')
+ // whyDidYouUpdate(React)
+ // }
+ if (this.responsive) {
+ const breakpoints = this.responsive.map(
+ breakpt => breakpt.breakpoint
+ )
+ // sort them in increasing order of their numerical value
+ breakpoints.sort((x, y) => x - y)
+
+ breakpoints.forEach((breakpoint, index) => {
+ // media query for each breakpoint
+ let bQuery
+ if (index === 0) {
+ bQuery = json2mq({ minWidth: 0, maxWidth: breakpoint })
+ } else {
+ bQuery = json2mq({
+ minWidth: breakpoints[index - 1] + 1,
+ maxWidth: breakpoint,
+ })
+ }
+ // when not using server side rendering
+ canUseDOM() &&
+ this.media(bQuery, () => {
+ this.setState({ breakpoint: breakpoint })
+ })
+ })
+
+ // Register media query for full screen. Need to support resize from small to large
+ // convert javascript object to media query string
+ const query = json2mq({ minWidth: breakpoints.slice(-1)[0] })
+
+ canUseDOM() &&
+ this.media(query, () => {
+ this.setState({ breakpoint: null })
+ })
+ }
+ },
+ beforeDestroy () {
+ this._responsiveMediaHandlers.forEach(function (obj) {
+ enquire.unregister(obj.query, obj.handler)
+ })
+ },
+
+ render () {
+ let settings
+ let newProps
+ if (this.breakpoint) {
+ newProps = this.responsive.filter(
+ resp => resp.breakpoint === this.breakpoint
+ )
+ settings =
+ newProps[0].settings === 'unslick'
+ ? 'unslick'
+ : { ...this.$props, ...newProps[0].settings }
+ } else {
+ settings = { ...this.$props }
+ }
+
+ // force scrolling by one if centerMode is on
+ if (settings.centerMode) {
+ if (
+ settings.slidesToScroll > 1 &&
+ process.env.NODE_ENV !== 'production'
+ ) {
+ console.warn(
+ `slidesToScroll should be equal to 1 in centerMode, you are using ${
+ settings.slidesToScroll
+ }`
+ )
+ }
+ settings.slidesToScroll = 1
+ }
+ // force showing one slide and scrolling by one if the fade mode is on
+ if (settings.fade) {
+ if (settings.slidesToShow > 1 && process.env.NODE_ENV !== 'production') {
+ console.warn(
+ `slidesToShow should be equal to 1 when fade is true, you're using ${
+ settings.slidesToShow
+ }`
+ )
+ }
+ if (
+ settings.slidesToScroll > 1 &&
+ process.env.NODE_ENV !== 'production'
+ ) {
+ console.warn(
+ `slidesToScroll should be equal to 1 when fade is true, you're using ${
+ settings.slidesToScroll
+ }`
+ )
+ }
+ settings.slidesToShow = 1
+ settings.slidesToScroll = 1
+ }
+
+ // makes sure that children is an array, even when there is only 1 child
+ let children = this.$slots.default || []
+
+ // Children may contain false or null, so we should filter them
+ // children may also contain string filled with spaces (in certain cases where we use jsx strings)
+ children = children.filter(child => {
+ if (typeof child === 'string') {
+ return !!child.trim()
+ }
+ return !!child
+ })
+
+ // rows and slidesPerRow logic is handled here
+ if (
+ settings.variableWidth &&
+ (settings.rows > 1 || settings.slidesPerRow > 1)
+ ) {
+ console.warn(
+ `variableWidth is not supported in case of rows > 1 or slidesPerRow > 1`
+ )
+ settings.variableWidth = false
+ }
+ const newChildren = []
+ let currentWidth = null
+ for (
+ let i = 0;
+ i < children.length;
+ i += settings.rows * settings.slidesPerRow
+ ) {
+ const newSlide = []
+ for (
+ let j = i;
+ j < i + settings.rows * settings.slidesPerRow;
+ j += settings.slidesPerRow
+ ) {
+ const row = []
+ for (let k = j; k < j + settings.slidesPerRow; k += 1) {
+ if (settings.variableWidth && getStyle(children[k])) {
+ currentWidth = getStyle(children[k]).width
+ }
+ if (k >= children.length) break
+ row.push(
+ cloneElement(children[k], {
+ key: 100 * i + 10 * j + k,
+ attrs: {
+ tabIndex: -1,
+ },
+ style: {
+ width: `${100 / settings.slidesPerRow}%`,
+ display: 'inline-block',
+ },
+ })
+ )
+ }
+ newSlide.push({row}
)
+ }
+ if (settings.variableWidth) {
+ newChildren.push(
+
+ {newSlide}
+
+ )
+ } else {
+ newChildren.push({newSlide}
)
+ }
+ }
+
+ if (settings === 'unslick') {
+ const className = 'regular slider ' + (this.className || '')
+ return {newChildren}
+ } else if (newChildren.length <= settings.slidesToShow) {
+ settings.unslick = true
+ }
+ const sliderProps = {
+ props: {
+ ...settings,
+ children: newChildren,
+ __propsSymbol__: Symbol(),
+ },
+ on: {
+ ...this.$listeners,
+ },
+ directives: [{
+ name: 'ant-ref',
+ value: this.innerSliderRefHandler,
+ }],
+ scopedSlots: this.$scopedSlots,
+ }
+ return (
+
+ )
+ },
+}
diff --git a/components/vc-slick/src/track.js b/components/vc-slick/src/track.js
new file mode 100644
index 0000000000..a2fa90ffdc
--- /dev/null
+++ b/components/vc-slick/src/track.js
@@ -0,0 +1,232 @@
+import classnames from 'classnames'
+import { cloneElement } from '../../_util/vnode'
+import { getStyle, getClass } from '../../_util/props-util'
+import {
+ lazyStartIndex,
+ lazyEndIndex,
+ getPreClones,
+} from './utils/innerSliderUtils'
+
+// given specifications/props for a slide, fetch all the classes that need to be applied to the slide
+const getSlideClasses = spec => {
+ let slickActive, slickCenter
+ let centerOffset, index
+
+ if (spec.rtl) {
+ index = spec.slideCount - 1 - spec.index
+ } else {
+ index = spec.index
+ }
+ const slickCloned = index < 0 || index >= spec.slideCount
+ if (spec.centerMode) {
+ centerOffset = Math.floor(spec.slidesToShow / 2)
+ slickCenter = (index - spec.currentSlide) % spec.slideCount === 0
+ if (
+ index > spec.currentSlide - centerOffset - 1 &&
+ index <= spec.currentSlide + centerOffset
+ ) {
+ slickActive = true
+ }
+ } else {
+ slickActive =
+ spec.currentSlide <= index &&
+ index < spec.currentSlide + spec.slidesToShow
+ }
+ const slickCurrent = index === spec.currentSlide
+ return {
+ 'slick-slide': true,
+ 'slick-active': slickActive,
+ 'slick-center': slickCenter,
+ 'slick-cloned': slickCloned,
+ 'slick-current': slickCurrent, // dubious in case of RTL
+ }
+}
+
+const getSlideStyle = function (spec) {
+ const style = {}
+
+ if (spec.variableWidth === undefined || spec.variableWidth === false) {
+ style.width = spec.slideWidth + (typeof spec.slideWidth === 'number' ? 'px' : '')
+ }
+
+ if (spec.fade) {
+ style.position = 'relative'
+ if (spec.vertical) {
+ style.top = -spec.index * parseInt(spec.slideHeight) + 'px'
+ } else {
+ style.left = -spec.index * parseInt(spec.slideWidth) + 'px'
+ }
+ style.opacity = spec.currentSlide === spec.index ? 1 : 0
+ style.transition =
+ 'opacity ' +
+ spec.speed +
+ 'ms ' +
+ spec.cssEase +
+ ', ' +
+ 'visibility ' +
+ spec.speed +
+ 'ms ' +
+ spec.cssEase
+ style.WebkitTransition =
+ 'opacity ' +
+ spec.speed +
+ 'ms ' +
+ spec.cssEase +
+ ', ' +
+ 'visibility ' +
+ spec.speed +
+ 'ms ' +
+ spec.cssEase
+ }
+
+ return style
+}
+
+const getKey = (child, fallbackKey) => child.key || (child.key === 0 && '0') || fallbackKey
+
+const renderSlides = function (spec, children, createElement) {
+ let key
+ const slides = []
+ const preCloneSlides = []
+ const postCloneSlides = []
+ const childrenCount = children.length
+ const startIndex = lazyStartIndex(spec)
+ const endIndex = lazyEndIndex(spec)
+
+ children.forEach((elem, index) => {
+ let child
+ const childOnClickOptions = {
+ message: 'children',
+ index: index,
+ slidesToScroll: spec.slidesToScroll,
+ currentSlide: spec.currentSlide,
+ }
+
+ // in case of lazyLoad, whether or not we want to fetch the slide
+ if (
+ !spec.lazyLoad ||
+ (spec.lazyLoad && spec.lazyLoadedList.indexOf(index) >= 0)
+ ) {
+ child = elem
+ } else {
+ child = createElement('div')
+ }
+ const childStyle = getSlideStyle({ ...spec, index })
+ const slideClass = getClass(child.context) || ''
+ let slideClasses = getSlideClasses({ ...spec, index })
+ // push a cloned element of the desired slide
+ slides.push(
+ cloneElement(child, {
+ key: 'original' + getKey(child, index),
+ attrs: {
+ tabIndex: '-1',
+ 'data-index': index,
+ 'aria-hidden': !slideClasses['slick-active'],
+ },
+ class: classnames(slideClasses, slideClass),
+ style: { outline: 'none', ...(getStyle(child.context) || {}), ...childStyle },
+ on: {
+ click: e => {
+ // child.props && child.props.onClick && child.props.onClick(e)
+ if (spec.focusOnSelect) {
+ spec.focusOnSelect(childOnClickOptions)
+ }
+ },
+ },
+ }, true)
+ )
+
+ // if slide needs to be precloned or postcloned
+ if (spec.infinite && spec.fade === false) {
+ const preCloneNo = childrenCount - index
+ if (
+ preCloneNo <= getPreClones(spec) &&
+ childrenCount !== spec.slidesToShow
+ ) {
+ key = -preCloneNo
+ if (key >= startIndex) {
+ child = elem
+ }
+ slideClasses = getSlideClasses({ ...spec, index: key })
+ preCloneSlides.push(
+ cloneElement(child, {
+ key: 'precloned' + getKey(child, key),
+ class: classnames(slideClasses, slideClass),
+ attrs: {
+ tabIndex: '-1',
+ 'data-index': key,
+ 'aria-hidden': !slideClasses['slick-active'],
+ },
+ style: { ...(getStyle(child.context) || {}), ...childStyle },
+ on: {
+ click: e => {
+ // child.props && child.props.onClick && child.props.onClick(e)
+ if (spec.focusOnSelect) {
+ spec.focusOnSelect(childOnClickOptions)
+ }
+ },
+ },
+ })
+ )
+ }
+
+ if (childrenCount !== spec.slidesToShow) {
+ key = childrenCount + index
+ if (key < endIndex) {
+ child = elem
+ }
+ slideClasses = getSlideClasses({ ...spec, index: key })
+ postCloneSlides.push(
+ cloneElement(child, {
+ key: 'postcloned' + getKey(child, key),
+ attrs: {
+ tabIndex: '-1',
+ 'data-index': key,
+ 'aria-hidden': !slideClasses['slick-active'],
+ },
+ class: classnames(slideClasses, slideClass),
+ style: { ...(getStyle(child.context) || {}), ...childStyle },
+ on: {
+ click: e => {
+ // child.props && child.props.onClick && child.props.onClick(e)
+ if (spec.focusOnSelect) {
+ spec.focusOnSelect(childOnClickOptions)
+ }
+ },
+ },
+ })
+ )
+ }
+ }
+ })
+ if (spec.rtl) {
+ return preCloneSlides.concat(slides, postCloneSlides).reverse()
+ } else {
+ return preCloneSlides.concat(slides, postCloneSlides)
+ }
+}
+
+export default {
+ functional: true,
+ render (createElement, context) {
+ const { props, listeners, children, data } = context
+ const slides = renderSlides(props, children, createElement)
+ const { mouseenter, mouseover, mouseleave } = listeners
+ const mouseEvents = { mouseenter, mouseover, mouseleave }
+ const trackProps = {
+ class: 'slick-track',
+ style: props.trackStyle,
+ on: {
+ ...mouseEvents,
+ },
+ directives: data.directives,
+ }
+ return (
+
+ {slides}
+
+ )
+ },
+}
diff --git a/components/vc-slick/src/utils/innerSliderUtils.js b/components/vc-slick/src/utils/innerSliderUtils.js
new file mode 100644
index 0000000000..92b43a8756
--- /dev/null
+++ b/components/vc-slick/src/utils/innerSliderUtils.js
@@ -0,0 +1,819 @@
+export const getOnDemandLazySlides = spec => {
+ const onDemandSlides = []
+ const startIndex = lazyStartIndex(spec)
+ const endIndex = lazyEndIndex(spec)
+ for (let slideIndex = startIndex; slideIndex < endIndex; slideIndex++) {
+ if (spec.lazyLoadedList.indexOf(slideIndex) < 0) {
+ onDemandSlides.push(slideIndex)
+ }
+ }
+ return onDemandSlides
+}
+
+// return list of slides that need to be present
+export const getRequiredLazySlides = spec => {
+ const requiredSlides = []
+ const startIndex = lazyStartIndex(spec)
+ const endIndex = lazyEndIndex(spec)
+ for (let slideIndex = startIndex; slideIndex < endIndex; slideIndex++) {
+ requiredSlides.push(slideIndex)
+ }
+ return requiredSlides
+}
+
+// startIndex that needs to be present
+export const lazyStartIndex = spec =>
+ spec.currentSlide - lazySlidesOnLeft(spec)
+export const lazyEndIndex = spec => spec.currentSlide + lazySlidesOnRight(spec)
+export const lazySlidesOnLeft = spec =>
+ spec.centerMode
+ ? Math.floor(spec.slidesToShow / 2) +
+ (parseInt(spec.centerPadding) > 0 ? 1 : 0)
+ : 0
+export const lazySlidesOnRight = spec =>
+ spec.centerMode
+ ? Math.floor((spec.slidesToShow - 1) / 2) +
+ 1 +
+ (parseInt(spec.centerPadding) > 0 ? 1 : 0)
+ : spec.slidesToShow
+
+// get width of an element
+export const getWidth = elem => (elem && elem.offsetWidth) || 0
+export const getHeight = elem => (elem && elem.offsetHeight) || 0
+export const getSwipeDirection = (touchObject, verticalSwiping = false) => {
+ let swipeAngle
+ const xDist = touchObject.startX - touchObject.curX
+ const yDist = touchObject.startY - touchObject.curY
+ const r = Math.atan2(yDist, xDist)
+ swipeAngle = Math.round(r * 180 / Math.PI)
+ if (swipeAngle < 0) {
+ swipeAngle = 360 - Math.abs(swipeAngle)
+ }
+ if (
+ (swipeAngle <= 45 && swipeAngle >= 0) ||
+ (swipeAngle <= 360 && swipeAngle >= 315)
+ ) {
+ return 'left'
+ }
+ if (swipeAngle >= 135 && swipeAngle <= 225) {
+ return 'right'
+ }
+ if (verticalSwiping === true) {
+ if (swipeAngle >= 35 && swipeAngle <= 135) {
+ return 'up'
+ } else {
+ return 'down'
+ }
+ }
+
+ return 'vertical'
+}
+
+// whether or not we can go next
+export const canGoNext = spec => {
+ let canGo = true
+ if (!spec.infinite) {
+ if (spec.centerMode && spec.currentSlide >= spec.slideCount - 1) {
+ canGo = false
+ } else if (
+ spec.slideCount <= spec.slidesToShow ||
+ spec.currentSlide >= spec.slideCount - spec.slidesToShow
+ ) {
+ canGo = false
+ }
+ }
+ return canGo
+}
+
+// given an object and a list of keys, return new object with given keys
+export const extractObject = (spec, keys) => {
+ const newObject = {}
+ keys.forEach(key => (newObject[key] = spec[key]))
+ return newObject
+}
+
+// get initialized state
+export const initializedState = spec => {
+ // spec also contains listRef, trackRef
+ const slideCount = spec.children.length
+ const listWidth = Math.ceil(getWidth(spec.listRef))
+ const trackWidth = Math.ceil(getWidth(spec.trackRef))
+ let slideWidth
+ if (!spec.vertical) {
+ let centerPaddingAdj = spec.centerMode && parseInt(spec.centerPadding) * 2
+ if (
+ typeof spec.centerPadding === 'string' &&
+ spec.centerPadding.slice(-1) === '%'
+ ) {
+ centerPaddingAdj *= listWidth / 100
+ }
+ slideWidth = Math.ceil((listWidth - centerPaddingAdj) / spec.slidesToShow)
+ } else {
+ slideWidth = listWidth
+ }
+ const slideHeight =
+ spec.listRef &&
+ getHeight(
+ spec.listRef.querySelector('[data-index="0"]')
+ )
+ const listHeight = slideHeight * spec.slidesToShow
+ let currentSlide =
+ spec.currentSlide === undefined ? spec.initialSlide : spec.currentSlide
+ if (spec.rtl && spec.currentSlide === undefined) {
+ currentSlide = slideCount - 1 - spec.initialSlide
+ }
+ const lazyLoadedList = spec.lazyLoadedList || []
+ const slidesToLoad = getOnDemandLazySlides(
+ { currentSlide, lazyLoadedList },
+ spec
+ )
+ lazyLoadedList.concat(slidesToLoad)
+
+ const state = {
+ slideCount,
+ slideWidth,
+ listWidth,
+ trackWidth,
+ currentSlide,
+ slideHeight,
+ listHeight,
+ lazyLoadedList,
+ }
+
+ if (spec.autoplaying === null && spec.autoplay) {
+ state['autoplaying'] = 'playing'
+ }
+
+ return state
+}
+
+export const slideHandler = spec => {
+ const {
+ waitForAnimate,
+ animating,
+ fade,
+ infinite,
+ index,
+ slideCount,
+ lazyLoadedList,
+ lazyLoad,
+ currentSlide,
+ centerMode,
+ slidesToScroll,
+ slidesToShow,
+ useCSS,
+ } = spec
+ if (waitForAnimate && animating) return {}
+ let animationSlide = index
+ let finalSlide
+ let animationLeft
+ let finalLeft
+ let state = {}
+ let nextState = {}
+ if (fade) {
+ if (!infinite && (index < 0 || index >= slideCount)) return {}
+ if (index < 0) {
+ animationSlide = index + slideCount
+ } else if (index >= slideCount) {
+ animationSlide = index - slideCount
+ }
+ if (lazyLoad && lazyLoadedList.indexOf(animationSlide) < 0) {
+ lazyLoadedList.push(animationSlide)
+ }
+ state = {
+ animating: true,
+ currentSlide: animationSlide,
+ lazyLoadedList,
+ }
+ nextState = { animating: false }
+ } else {
+ finalSlide = animationSlide
+ if (animationSlide < 0) {
+ finalSlide = animationSlide + slideCount
+ if (!infinite) finalSlide = 0
+ else if (slideCount % slidesToScroll !== 0) { finalSlide = slideCount - slideCount % slidesToScroll }
+ } else if (!canGoNext(spec) && animationSlide > currentSlide) {
+ animationSlide = finalSlide = currentSlide
+ } else if (centerMode && animationSlide >= slideCount) {
+ animationSlide = infinite ? slideCount : slideCount - 1
+ finalSlide = infinite ? 0 : slideCount - 1
+ } else if (animationSlide >= slideCount) {
+ finalSlide = animationSlide - slideCount
+ if (!infinite) finalSlide = slideCount - slidesToShow
+ else if (slideCount % slidesToScroll !== 0) finalSlide = 0
+ }
+ animationLeft = getTrackLeft({ ...spec, slideIndex: animationSlide })
+ finalLeft = getTrackLeft({ ...spec, slideIndex: finalSlide })
+ if (!infinite) {
+ if (animationLeft === finalLeft) animationSlide = finalSlide
+ animationLeft = finalLeft
+ }
+ lazyLoad &&
+ lazyLoadedList.concat(
+ getOnDemandLazySlides({ ...spec, currentSlide: animationSlide })
+ )
+ if (!useCSS) {
+ state = {
+ currentSlide: finalSlide,
+ trackStyle: getTrackCSS({ ...spec, left: finalLeft }),
+ lazyLoadedList,
+ }
+ } else {
+ state = {
+ animating: true,
+ currentSlide: finalSlide,
+ trackStyle: getTrackAnimateCSS({ ...spec, left: animationLeft }),
+ lazyLoadedList,
+ }
+ nextState = {
+ animating: false,
+ currentSlide: finalSlide,
+ trackStyle: getTrackCSS({ ...spec, left: finalLeft }),
+ swipeLeft: null,
+ }
+ }
+ }
+ return { state, nextState }
+}
+
+export const changeSlide = (spec, options) => {
+ let previousInt, slideOffset, targetSlide
+ const {
+ slidesToScroll,
+ slidesToShow,
+ slideCount,
+ currentSlide,
+ lazyLoad,
+ infinite,
+ } = spec
+ const unevenOffset = slideCount % slidesToScroll !== 0
+ const indexOffset = unevenOffset ? 0 : (slideCount - currentSlide) % slidesToScroll
+
+ if (options.message === 'previous') {
+ slideOffset = indexOffset === 0 ? slidesToScroll : slidesToShow - indexOffset
+ targetSlide = currentSlide - slideOffset
+ if (lazyLoad && !infinite) {
+ previousInt = currentSlide - slideOffset
+ targetSlide = previousInt === -1 ? slideCount - 1 : previousInt
+ }
+ } else if (options.message === 'next') {
+ slideOffset = indexOffset === 0 ? slidesToScroll : indexOffset
+ targetSlide = currentSlide + slideOffset
+ if (lazyLoad && !infinite) {
+ targetSlide = (currentSlide + slidesToScroll) % slideCount + indexOffset
+ }
+ } else if (options.message === 'dots') {
+ // Click on dots
+ targetSlide = options.index * options.slidesToScroll
+ if (targetSlide === options.currentSlide) {
+ return null
+ }
+ } else if (options.message === 'children') {
+ // Click on the slides
+ targetSlide = options.index
+ if (targetSlide === options.currentSlide) {
+ return null
+ }
+ if (infinite) {
+ const direction = siblingDirection({ ...spec, targetSlide })
+ if (targetSlide > options.currentSlide && direction === 'left') {
+ targetSlide = targetSlide - slideCount
+ } else if (targetSlide < options.currentSlide && direction === 'right') {
+ targetSlide = targetSlide + slideCount
+ }
+ }
+ } else if (options.message === 'index') {
+ targetSlide = Number(options.index)
+ if (targetSlide === options.currentSlide) {
+ return null
+ }
+ }
+ return targetSlide
+}
+export const keyHandler = (e, accessibility, rtl) => {
+ if (e.target.tagName.match('TEXTAREA|INPUT|SELECT') || !accessibility) { return '' }
+ if (e.keyCode === 37) return rtl ? 'next' : 'previous'
+ if (e.keyCode === 39) return rtl ? 'previous' : 'next'
+ return ''
+}
+
+export const swipeStart = (e, swipe, draggable) => {
+ e.target.tagName === 'IMG' && e.preventDefault()
+ if (!swipe || (!draggable && e.type.indexOf('mouse') !== -1)) return ''
+ return {
+ dragging: true,
+ touchObject: {
+ startX: e.touches ? e.touches[0].pageX : e.clientX,
+ startY: e.touches ? e.touches[0].pageY : e.clientY,
+ curX: e.touches ? e.touches[0].pageX : e.clientX,
+ curY: e.touches ? e.touches[0].pageY : e.clientY,
+ },
+ }
+}
+export const swipeMove = (e, spec) => {
+ // spec also contains, trackRef and slideIndex
+ const {
+ scrolling,
+ animating,
+ vertical,
+ swipeToSlide,
+ verticalSwiping,
+ rtl,
+ currentSlide,
+ edgeFriction,
+ edgeDragged,
+ onEdge,
+ swiped,
+ swiping,
+ slideCount,
+ slidesToScroll,
+ infinite,
+ touchObject,
+ swipeEvent,
+ listHeight,
+ listWidth,
+ } = spec
+ if (scrolling) return
+ if (animating) return e.preventDefault()
+ if (vertical && swipeToSlide && verticalSwiping) e.preventDefault()
+ let swipeLeft
+ let state = {}
+ const curLeft = getTrackLeft(spec)
+ touchObject.curX = e.touches ? e.touches[0].pageX : e.clientX
+ touchObject.curY = e.touches ? e.touches[0].pageY : e.clientY
+ touchObject.swipeLength = Math.round(
+ Math.sqrt(Math.pow(touchObject.curX - touchObject.startX, 2))
+ )
+ const verticalSwipeLength = Math.round(
+ Math.sqrt(Math.pow(touchObject.curY - touchObject.startY, 2))
+ )
+ if (!verticalSwiping && !swiping && verticalSwipeLength > 10) {
+ return { scrolling: true }
+ }
+ if (verticalSwiping) touchObject.swipeLength = verticalSwipeLength
+ let positionOffset =
+ (!rtl ? 1 : -1) * (touchObject.curX > touchObject.startX ? 1 : -1)
+ if (verticalSwiping) { positionOffset = touchObject.curY > touchObject.startY ? 1 : -1 }
+
+ const dotCount = Math.ceil(slideCount / slidesToScroll)
+ const swipeDirection = getSwipeDirection(spec.touchObject, verticalSwiping)
+ let touchSwipeLength = touchObject.swipeLength
+ if (!infinite) {
+ if (
+ (currentSlide === 0 && swipeDirection === 'right') ||
+ (currentSlide + 1 >= dotCount && swipeDirection === 'left') ||
+ (!canGoNext(spec) && swipeDirection === 'left')
+ ) {
+ touchSwipeLength = touchObject.swipeLength * edgeFriction
+ if (edgeDragged === false && onEdge) {
+ onEdge(swipeDirection)
+ state['edgeDragged'] = true
+ }
+ }
+ }
+ if (!swiped && swipeEvent) {
+ swipeEvent(swipeDirection)
+ state['swiped'] = true
+ }
+ if (!vertical) {
+ if (!rtl) {
+ swipeLeft = curLeft + touchSwipeLength * positionOffset
+ } else {
+ swipeLeft = curLeft - touchSwipeLength * positionOffset
+ }
+ } else {
+ swipeLeft =
+ curLeft + touchSwipeLength * (listHeight / listWidth) * positionOffset
+ }
+ if (verticalSwiping) {
+ swipeLeft = curLeft + touchSwipeLength * positionOffset
+ }
+ state = {
+ ...state,
+ touchObject,
+ swipeLeft,
+ trackStyle: getTrackCSS({ ...spec, left: swipeLeft }),
+ }
+ if (
+ Math.abs(touchObject.curX - touchObject.startX) <
+ Math.abs(touchObject.curY - touchObject.startY) * 0.8
+ ) {
+ return state
+ }
+ if (touchObject.swipeLength > 10) {
+ state['swiping'] = true
+ e.preventDefault()
+ }
+ return state
+}
+export const swipeEnd = (e, spec) => {
+ const {
+ dragging,
+ swipe,
+ touchObject,
+ listWidth,
+ touchThreshold,
+ verticalSwiping,
+ listHeight,
+ currentSlide,
+ swipeToSlide,
+ scrolling,
+ onSwipe,
+ } = spec
+ if (!dragging) {
+ if (swipe) e.preventDefault()
+ return {}
+ }
+ const minSwipe = verticalSwiping
+ ? listHeight / touchThreshold
+ : listWidth / touchThreshold
+ const swipeDirection = getSwipeDirection(touchObject, verticalSwiping)
+ // reset the state of touch related state variables.
+ const state = {
+ dragging: false,
+ edgeDragged: false,
+ scrolling: false,
+ swiping: false,
+ swiped: false,
+ swipeLeft: null,
+ touchObject: {},
+ }
+ if (scrolling) {
+ return state
+ }
+ if (!touchObject.swipeLength) {
+ return state
+ }
+ if (touchObject.swipeLength > minSwipe) {
+ e.preventDefault()
+ if (onSwipe) {
+ onSwipe(swipeDirection)
+ }
+ let slideCount, newSlide
+ switch (swipeDirection) {
+ case 'left':
+ case 'up':
+ newSlide = currentSlide + getSlideCount(spec)
+ slideCount = swipeToSlide ? checkNavigable(spec, newSlide) : newSlide
+ state['currentDirection'] = 0
+ break
+ case 'right':
+ case 'down':
+ newSlide = currentSlide - getSlideCount(spec)
+ slideCount = swipeToSlide ? checkNavigable(spec, newSlide) : newSlide
+ state['currentDirection'] = 1
+ break
+ default:
+ slideCount = currentSlide
+ }
+ state['triggerSlideHandler'] = slideCount
+ } else {
+ // Adjust the track back to it's original position.
+ const currentLeft = getTrackLeft(spec)
+ state['trackStyle'] = getTrackAnimateCSS({ ...spec, left: currentLeft })
+ }
+ return state
+}
+export const getNavigableIndexes = spec => {
+ const max = spec.infinite ? spec.slideCount * 2 : spec.slideCount
+ let breakpoint = spec.infinite ? spec.slidesToShow * -1 : 0
+ let counter = spec.infinite ? spec.slidesToShow * -1 : 0
+ const indexes = []
+ while (breakpoint < max) {
+ indexes.push(breakpoint)
+ breakpoint = counter + spec.slidesToScroll
+ counter += Math.min(spec.slidesToScroll, spec.slidesToShow)
+ }
+ return indexes
+}
+export const checkNavigable = (spec, index) => {
+ const navigables = getNavigableIndexes(spec)
+ let prevNavigable = 0
+ if (index > navigables[navigables.length - 1]) {
+ index = navigables[navigables.length - 1]
+ } else {
+ for (const n in navigables) {
+ if (index < navigables[n]) {
+ index = prevNavigable
+ break
+ }
+ prevNavigable = navigables[n]
+ }
+ }
+ return index
+}
+export const getSlideCount = spec => {
+ const centerOffset = spec.centerMode
+ ? spec.slideWidth * Math.floor(spec.slidesToShow / 2)
+ : 0
+ if (spec.swipeToSlide) {
+ let swipedSlide
+ const slickList = spec.listRef
+ const slides = slickList.querySelectorAll('.slick-slide')
+ Array.from(slides).every(slide => {
+ if (!spec.vertical) {
+ if (
+ slide.offsetLeft - centerOffset + getWidth(slide) / 2 >
+ spec.swipeLeft * -1
+ ) {
+ swipedSlide = slide
+ return false
+ }
+ } else {
+ if (slide.offsetTop + getHeight(slide) / 2 > spec.swipeLeft * -1) {
+ swipedSlide = slide
+ return false
+ }
+ }
+
+ return true
+ })
+
+ if (!swipedSlide) {
+ return 0
+ }
+ const currentIndex =
+ spec.rtl === true
+ ? spec.slideCount - spec.currentSlide
+ : spec.currentSlide
+ const slidesTraversed =
+ Math.abs(swipedSlide.dataset.index - currentIndex) || 1
+ return slidesTraversed
+ } else {
+ return spec.slidesToScroll
+ }
+}
+
+export const checkSpecKeys = (spec, keysArray) =>
+ keysArray.reduce((value, key) => value && spec.hasOwnProperty(key), true)
+ ? null
+ : console.error('Keys Missing:', spec)
+
+export const getTrackCSS = spec => {
+ checkSpecKeys(spec, [
+ 'left',
+ 'variableWidth',
+ 'slideCount',
+ 'slidesToShow',
+ 'slideWidth',
+ ])
+ let trackWidth, trackHeight
+ const trackChildren = spec.slideCount + 2 * spec.slidesToShow
+ if (!spec.vertical) {
+ trackWidth = getTotalSlides(spec) * spec.slideWidth
+ } else {
+ trackHeight = trackChildren * spec.slideHeight
+ }
+ let style = {
+ opacity: 1,
+ transition: '',
+ WebkitTransition: '',
+ }
+ if (spec.useTransform) {
+ const WebkitTransform = !spec.vertical
+ ? 'translate3d(' + spec.left + 'px, 0px, 0px)'
+ : 'translate3d(0px, ' + spec.left + 'px, 0px)'
+ const transform = !spec.vertical
+ ? 'translate3d(' + spec.left + 'px, 0px, 0px)'
+ : 'translate3d(0px, ' + spec.left + 'px, 0px)'
+ const msTransform = !spec.vertical
+ ? 'translateX(' + spec.left + 'px)'
+ : 'translateY(' + spec.left + 'px)'
+ style = {
+ ...style,
+ WebkitTransform,
+ transform,
+ msTransform,
+ }
+ } else {
+ if (spec.vertical) {
+ style['top'] = spec.left
+ } else {
+ style['left'] = spec.left
+ }
+ }
+ if (spec.fade) style = { opacity: 1 }
+ if (trackWidth) style.width = trackWidth + 'px'
+ if (trackHeight) style.height = trackHeight + 'px'
+
+ // Fallback for IE8
+ if (window && !window.addEventListener && window.attachEvent) {
+ if (!spec.vertical) {
+ style.marginLeft = spec.left + 'px'
+ } else {
+ style.marginTop = spec.left + 'px'
+ }
+ }
+
+ return style
+}
+export const getTrackAnimateCSS = spec => {
+ checkSpecKeys(spec, [
+ 'left',
+ 'variableWidth',
+ 'slideCount',
+ 'slidesToShow',
+ 'slideWidth',
+ 'speed',
+ 'cssEase',
+ ])
+ const style = getTrackCSS(spec)
+ // useCSS is true by default so it can be undefined
+ if (spec.useTransform) {
+ style.WebkitTransition =
+ '-webkit-transform ' + spec.speed + 'ms ' + spec.cssEase
+ style.transition = 'transform ' + spec.speed + 'ms ' + spec.cssEase
+ } else {
+ if (spec.vertical) {
+ style.transition = 'top ' + spec.speed + 'ms ' + spec.cssEase
+ } else {
+ style.transition = 'left ' + spec.speed + 'ms ' + spec.cssEase
+ }
+ }
+ return style
+}
+export const getTrackLeft = spec => {
+ if (spec.unslick) {
+ return 0
+ }
+
+ checkSpecKeys(spec, [
+ 'slideIndex',
+ 'trackRef',
+ 'infinite',
+ 'centerMode',
+ 'slideCount',
+ 'slidesToShow',
+ 'slidesToScroll',
+ 'slideWidth',
+ 'listWidth',
+ 'variableWidth',
+ 'slideHeight',
+ ])
+
+ const {
+ slideIndex,
+ trackRef,
+ infinite,
+ centerMode,
+ slideCount,
+ slidesToShow,
+ slidesToScroll,
+ slideWidth,
+ listWidth,
+ variableWidth,
+ slideHeight,
+ fade,
+ vertical,
+ } = spec
+
+ let slideOffset = 0
+ let targetLeft
+ let targetSlide
+ let verticalOffset = 0
+
+ if (fade || spec.slideCount === 1) {
+ return 0
+ }
+
+ let slidesToOffset = 0
+ if (infinite) {
+ slidesToOffset = -getPreClones(spec) // bring active slide to the beginning of visual area
+ // if next scroll doesn't have enough children, just reach till the end of original slides instead of shifting slidesToScroll children
+ if (
+ slideCount % slidesToScroll !== 0 &&
+ slideIndex + slidesToScroll > slideCount
+ ) {
+ slidesToOffset = -(slideIndex > slideCount
+ ? slidesToShow - (slideIndex - slideCount)
+ : slideCount % slidesToScroll)
+ }
+ // shift current slide to center of the frame
+ if (centerMode) {
+ slidesToOffset += parseInt(slidesToShow / 2)
+ }
+ } else {
+ if (
+ slideCount % slidesToScroll !== 0 &&
+ slideIndex + slidesToScroll > slideCount
+ ) {
+ slidesToOffset = slidesToShow - slideCount % slidesToScroll
+ }
+ if (centerMode) {
+ slidesToOffset = parseInt(slidesToShow / 2)
+ }
+ }
+ slideOffset = slidesToOffset * slideWidth
+ verticalOffset = slidesToOffset * slideHeight
+
+ if (!vertical) {
+ targetLeft = slideIndex * slideWidth * -1 + slideOffset
+ } else {
+ targetLeft = slideIndex * slideHeight * -1 + verticalOffset
+ }
+
+ if (variableWidth === true) {
+ let targetSlideIndex
+ const trackElem = trackRef
+ targetSlideIndex = slideIndex + getPreClones(spec)
+ targetSlide = trackElem && trackElem.childNodes[targetSlideIndex]
+ targetLeft = targetSlide ? targetSlide.offsetLeft * -1 : 0
+ if (centerMode === true) {
+ targetSlideIndex = infinite
+ ? slideIndex + getPreClones(spec)
+ : slideIndex
+ targetSlide = trackElem && trackElem.children[targetSlideIndex]
+ targetLeft = 0
+ for (let slide = 0; slide < targetSlideIndex; slide++) {
+ targetLeft -=
+ trackElem &&
+ trackElem.children[slide] &&
+ trackElem.children[slide].offsetWidth
+ }
+ targetLeft -= parseInt(spec.centerPadding)
+ targetLeft += targetSlide && (listWidth - targetSlide.offsetWidth) / 2
+ }
+ }
+
+ return targetLeft
+}
+
+export const getPreClones = spec => {
+ if (spec.unslick || !spec.infinite) {
+ return 0
+ }
+ if (spec.variableWidth) {
+ return spec.slideCount
+ }
+ return spec.slidesToShow + (spec.centerMode ? 1 : 0)
+}
+
+export const getPostClones = spec => {
+ if (spec.unslick || !spec.infinite) {
+ return 0
+ }
+ return spec.slideCount
+}
+
+export const getTotalSlides = spec =>
+ spec.slideCount === 1
+ ? 1
+ : getPreClones(spec) + spec.slideCount + getPostClones(spec)
+export const siblingDirection = spec => {
+ if (spec.targetSlide > spec.currentSlide) {
+ if (spec.targetSlide > spec.currentSlide + slidesOnRight(spec)) {
+ return 'left'
+ }
+ return 'right'
+ } else {
+ if (spec.targetSlide < spec.currentSlide - slidesOnLeft(spec)) {
+ return 'right'
+ }
+ return 'left'
+ }
+}
+
+export const slidesOnRight = ({
+ slidesToShow,
+ centerMode,
+ rtl,
+ centerPadding,
+}) => {
+ // returns no of slides on the right of active slide
+ if (centerMode) {
+ let right = (slidesToShow - 1) / 2 + 1
+ if (parseInt(centerPadding) > 0) right += 1
+ if (rtl && slidesToShow % 2 === 0) right += 1
+ return right
+ }
+ if (rtl) {
+ return 0
+ }
+ return slidesToShow - 1
+}
+
+export const slidesOnLeft = ({
+ slidesToShow,
+ centerMode,
+ rtl,
+ centerPadding,
+}) => {
+ // returns no of slides on the left of active slide
+ if (centerMode) {
+ let left = (slidesToShow - 1) / 2 + 1
+ if (parseInt(centerPadding) > 0) left += 1
+ if (!rtl && slidesToShow % 2 === 0) left += 1
+ return left
+ }
+ if (rtl) {
+ return slidesToShow - 1
+ }
+ return 0
+}
+
+export const canUseDOM = () =>
+ !!(
+ typeof window !== 'undefined' &&
+ window.document &&
+ window.document.createElement
+ )
diff --git a/package.json b/package.json
index 589a594a6f..644bc9c78d 100644
--- a/package.json
+++ b/package.json
@@ -162,12 +162,14 @@
"dom-closest": "^0.2.0",
"dom-scroll-into-view": "^1.2.1",
"enquire.js": "^2.1.6",
+ "json2mq": "^0.2.0",
"is-negative-zero": "^2.0.0",
"lodash": "^4.17.5",
"moment": "^2.21.0",
"omit.js": "^1.0.0",
+ "resize-observer-polyfill": "^1.5.0",
"shallow-equal": "^1.0.0",
"shallowequal": "^1.0.2",
"warning": "^3.0.0"
}
-}
\ No newline at end of file
+}
diff --git a/site/components.js b/site/components.js
index 2bfd38350e..b1bc8ad1ca 100644
--- a/site/components.js
+++ b/site/components.js
@@ -12,7 +12,7 @@ import {
Calendar,
Card,
Collapse,
- // Carousel,
+ Carousel,
Cascader,
Checkbox,
Col,
@@ -75,7 +75,7 @@ Vue.component(Card.Meta.name, Card.Meta)
Vue.component(Card.Grid.name, Card.Grid)
Vue.component(Collapse.name, Collapse)
Vue.component(Collapse.Panel.name, Collapse.Panel)
-// Vue.component(Carousel.name, Carousel)
+Vue.component(Carousel.name, Carousel)
Vue.component(Cascader.name, Cascader)
Vue.component(Checkbox.name, Checkbox)
Vue.component(Checkbox.Group.name, Checkbox.Group)
diff --git a/site/demo.js b/site/demo.js
index 5e434fd81e..bc53aa35f9 100644
--- a/site/demo.js
+++ b/site/demo.js
@@ -276,6 +276,12 @@ export default {
type: 'Data Entry',
title: 'Upload',
},
+ carousel: {
+ category: 'Components',
+ type: 'Data Display',
+ title: 'Carousel',
+ subtitle: '走马灯',
+ },
tree: {
category: 'Components',
subtitle: '树形控件',
diff --git a/site/demoRoutes.js b/site/demoRoutes.js
index 4c7b8f7152..c79eab4e7d 100644
--- a/site/demoRoutes.js
+++ b/site/demoRoutes.js
@@ -399,4 +399,12 @@ export default [
path: 'list-cn',
component: () => import('../components/list/demo/index.vue'),
},
+ {
+ path: 'carousel',
+ component: () => import('../components/carousel/demo/index.vue'),
+ },
+ {
+ path: 'carousel-cn',
+ component: () => import('../components/carousel/demo/index.vue'),
+ },
]
diff --git a/site/dev.js b/site/dev.js
index c171044398..0d84c3138e 100644
--- a/site/dev.js
+++ b/site/dev.js
@@ -10,7 +10,7 @@ import Api from './components/api'
import './components'
import demoBox from './components/demoBox'
import demoContainer from './components/demoContainer'
-import Test from '../components/upload/demo/index'
+import Test from '../components/carousel/demo/index'
Vue.use(VueClipboard)
Vue.use(VueRouter)
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index 4c6bdbab2b..820f34a13b 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -18,6 +18,7 @@ Array [
"Calendar",
"Card",
"Collapse",
+ "Carousel",
"Cascader",
"Checkbox",
"Col",