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 + + + +``` 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 + + + +``` 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 + + + +``` 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 + + + +``` 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 @@ + + + +Generated by Fontastic.me + + + + + + + + + + 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

+
+
+

2

+

Hello

+
+
+

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 ( +
+
    {dots}
+
+ ) + }, + }, + } + 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

+ +
+

100

+
+
+

200

+
+
+

75

+
+
+

300

+
+
+

225

+
+
+

175

+
+
+
+ ) + }, +} 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
    {dots}
+ // }), + 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
      {dots}
    + }, + }, + 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.children} + +
    + {!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",