From edddbd982a279b62229ce825855c14c556866ece Mon Sep 17 00:00:00 2001 From: wangxueliang Date: Fri, 20 Jul 2018 16:13:21 +0800 Subject: [PATCH] feat: add carousel --- .jest.js | 3 +- .../__tests__/__snapshots__/demo.test.js.snap | 318 +++++++ components/carousel/__tests__/demo.test.js | 3 + components/carousel/__tests__/index.test.js | 87 ++ components/carousel/demo/autoplay.md | 38 + components/carousel/demo/basic.md | 43 + components/carousel/demo/customPaging.md | 73 ++ components/carousel/demo/fade.md | 38 + components/carousel/demo/index.vue | 53 ++ components/carousel/demo/vertical.md | 38 + components/carousel/index.en-US.md | 21 + components/carousel/index.jsx | 159 ++++ components/carousel/index.zh-CN.md | 21 + components/carousel/style/index.js | 2 + components/carousel/style/index.less | 210 +++++ components/index.js | 4 +- components/style.js | 2 + components/vc-slick/assets/ajax-loader.gif | Bin 0 -> 4178 bytes components/vc-slick/assets/docs.less | 116 +++ components/vc-slick/assets/fonts/slick.eot | Bin 0 -> 2048 bytes components/vc-slick/assets/fonts/slick.svg | 14 + components/vc-slick/assets/fonts/slick.ttf | Bin 0 -> 1892 bytes components/vc-slick/assets/fonts/slick.woff | Bin 0 -> 1380 bytes .../assets/img/react-slick/abstract01.jpg | Bin 0 -> 26139 bytes .../assets/img/react-slick/abstract02.jpg | Bin 0 -> 10288 bytes .../assets/img/react-slick/abstract03.jpg | Bin 0 -> 5705 bytes .../assets/img/react-slick/abstract04.jpg | Bin 0 -> 34603 bytes components/vc-slick/assets/index.less | 3 + components/vc-slick/assets/slick-theme.less | 204 +++++ components/vc-slick/assets/slick.less | 119 +++ components/vc-slick/demo/AdaptiveHeight.jsx | 45 + components/vc-slick/demo/AppendDots.jsx | 69 ++ components/vc-slick/demo/AsNavFor.jsx | 75 ++ components/vc-slick/demo/AutoPlay.jsx | 44 + components/vc-slick/demo/AutoPlayMethods.jsx | 59 ++ components/vc-slick/demo/CenterMode.jsx | 42 + components/vc-slick/demo/CustomArrows.jsx | 62 ++ components/vc-slick/demo/CustomPaging.jsx | 50 + components/vc-slick/demo/CustomSlides.jsx | 40 + components/vc-slick/demo/DynamicSlides.jsx | 43 + components/vc-slick/demo/Fade.jsx | 41 + components/vc-slick/demo/FocusOnSelect.jsx | 42 + components/vc-slick/demo/LazyLoad.jsx | 42 + components/vc-slick/demo/MultipleItems.jsx | 50 + components/vc-slick/demo/MultipleRows.jsx | 74 ++ components/vc-slick/demo/PauseOnHover.jsx | 43 + .../vc-slick/demo/PreviousNextMethods.jsx | 58 ++ components/vc-slick/demo/Resizable.jsx | 87 ++ components/vc-slick/demo/Responsive.jsx | 74 ++ components/vc-slick/demo/Rtl.jsx | 43 + components/vc-slick/demo/SimpleSlider.jsx | 41 + components/vc-slick/demo/SlickGoTo.jsx | 58 ++ components/vc-slick/demo/SlideChangeHooks.jsx | 55 ++ components/vc-slick/demo/SwipeToSlide.jsx | 55 ++ components/vc-slick/demo/UnevenSetsFinite.jsx | 41 + .../vc-slick/demo/UnevenSetsInfinite.jsx | 41 + components/vc-slick/demo/VariableWidth.jsx | 43 + components/vc-slick/demo/VerticalMode.jsx | 48 + .../vc-slick/demo/VerticalSwipeToSlide.jsx | 49 + components/vc-slick/demo/config.js | 4 + components/vc-slick/demo/imglist.js | 11 + components/vc-slick/demo/index.jsx | 67 ++ components/vc-slick/src/arrows.js | 129 +++ components/vc-slick/src/default-props.js | 69 ++ components/vc-slick/src/dots.js | 84 ++ components/vc-slick/src/index.js | 3 + components/vc-slick/src/initial-state.js | 26 + components/vc-slick/src/inner-slider.js | 851 ++++++++++++++++++ components/vc-slick/src/slider.js | 241 +++++ components/vc-slick/src/track.js | 232 +++++ .../vc-slick/src/utils/innerSliderUtils.js | 819 +++++++++++++++++ package.json | 4 +- site/components.js | 4 +- site/demo.js | 6 + site/demoRoutes.js | 8 + site/dev.js | 2 +- tests/__snapshots__/index.test.js.snap | 1 + 77 files changed, 5638 insertions(+), 6 deletions(-) create mode 100644 components/carousel/__tests__/__snapshots__/demo.test.js.snap create mode 100644 components/carousel/__tests__/demo.test.js create mode 100644 components/carousel/__tests__/index.test.js create mode 100644 components/carousel/demo/autoplay.md create mode 100644 components/carousel/demo/basic.md create mode 100644 components/carousel/demo/customPaging.md create mode 100644 components/carousel/demo/fade.md create mode 100644 components/carousel/demo/index.vue create mode 100644 components/carousel/demo/vertical.md create mode 100644 components/carousel/index.en-US.md create mode 100644 components/carousel/index.jsx create mode 100644 components/carousel/index.zh-CN.md create mode 100644 components/carousel/style/index.js create mode 100644 components/carousel/style/index.less create mode 100644 components/vc-slick/assets/ajax-loader.gif create mode 100644 components/vc-slick/assets/docs.less create mode 100644 components/vc-slick/assets/fonts/slick.eot create mode 100644 components/vc-slick/assets/fonts/slick.svg create mode 100644 components/vc-slick/assets/fonts/slick.ttf create mode 100644 components/vc-slick/assets/fonts/slick.woff create mode 100644 components/vc-slick/assets/img/react-slick/abstract01.jpg create mode 100644 components/vc-slick/assets/img/react-slick/abstract02.jpg create mode 100644 components/vc-slick/assets/img/react-slick/abstract03.jpg create mode 100644 components/vc-slick/assets/img/react-slick/abstract04.jpg create mode 100644 components/vc-slick/assets/index.less create mode 100644 components/vc-slick/assets/slick-theme.less create mode 100644 components/vc-slick/assets/slick.less create mode 100644 components/vc-slick/demo/AdaptiveHeight.jsx create mode 100644 components/vc-slick/demo/AppendDots.jsx create mode 100644 components/vc-slick/demo/AsNavFor.jsx create mode 100644 components/vc-slick/demo/AutoPlay.jsx create mode 100644 components/vc-slick/demo/AutoPlayMethods.jsx create mode 100644 components/vc-slick/demo/CenterMode.jsx create mode 100644 components/vc-slick/demo/CustomArrows.jsx create mode 100644 components/vc-slick/demo/CustomPaging.jsx create mode 100644 components/vc-slick/demo/CustomSlides.jsx create mode 100644 components/vc-slick/demo/DynamicSlides.jsx create mode 100644 components/vc-slick/demo/Fade.jsx create mode 100644 components/vc-slick/demo/FocusOnSelect.jsx create mode 100644 components/vc-slick/demo/LazyLoad.jsx create mode 100644 components/vc-slick/demo/MultipleItems.jsx create mode 100644 components/vc-slick/demo/MultipleRows.jsx create mode 100644 components/vc-slick/demo/PauseOnHover.jsx create mode 100644 components/vc-slick/demo/PreviousNextMethods.jsx create mode 100644 components/vc-slick/demo/Resizable.jsx create mode 100644 components/vc-slick/demo/Responsive.jsx create mode 100644 components/vc-slick/demo/Rtl.jsx create mode 100644 components/vc-slick/demo/SimpleSlider.jsx create mode 100644 components/vc-slick/demo/SlickGoTo.jsx create mode 100644 components/vc-slick/demo/SlideChangeHooks.jsx create mode 100644 components/vc-slick/demo/SwipeToSlide.jsx create mode 100644 components/vc-slick/demo/UnevenSetsFinite.jsx create mode 100644 components/vc-slick/demo/UnevenSetsInfinite.jsx create mode 100644 components/vc-slick/demo/VariableWidth.jsx create mode 100644 components/vc-slick/demo/VerticalMode.jsx create mode 100644 components/vc-slick/demo/VerticalSwipeToSlide.jsx create mode 100644 components/vc-slick/demo/config.js create mode 100644 components/vc-slick/demo/imglist.js create mode 100644 components/vc-slick/demo/index.jsx create mode 100644 components/vc-slick/src/arrows.js create mode 100644 components/vc-slick/src/default-props.js create mode 100644 components/vc-slick/src/dots.js create mode 100644 components/vc-slick/src/index.js create mode 100644 components/vc-slick/src/initial-state.js create mode 100644 components/vc-slick/src/inner-slider.js create mode 100644 components/vc-slick/src/slider.js create mode 100644 components/vc-slick/src/track.js create mode 100644 components/vc-slick/src/utils/innerSliderUtils.js diff --git a/.jest.js b/.jest.js index db17b2570..fa5d4800a 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 000000000..bae53c189 --- /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 000000000..526e3d1e0 --- /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 000000000..03e17e5fb --- /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 000000000..481973dd5 --- /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 000000000..fae371f12 --- /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 000000000..2e9b3c71a --- /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 000000000..827195189 --- /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 000000000..4617aef87 --- /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 000000000..9950956dc --- /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 000000000..bad169467 --- /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 000000000..4e97cf5ec --- /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 000000000..a5dc70a42 --- /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 000000000..cf31ed80f --- /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 000000000..613f42bcb --- /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 5e6fce8f8..e8a5d4a2c 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 416939770..4c2d945b2 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 0000000000000000000000000000000000000000..e0e6e9760bc04861cc4771e327f22ed7962e0858 GIT binary patch literal 4178 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 literal 0 HcmV?d00001 diff --git a/components/vc-slick/assets/docs.less b/components/vc-slick/assets/docs.less new file mode 100644 index 000000000..5e9bad5a1 --- /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 0000000000000000000000000000000000000000..2cbab9ca97723bc24c50315a0a9bd155db4e0aa5 GIT binary patch literal 2048 zcmcgtO>7%Q6n^9V^B-}NZk*EEVeMi&O%prb7zafaL6A786eTJs%>e;R;y4y|oY<}i z5)vjt;=&>7feV}}ai#*PAS5oRLOp@#1tg{ z_p`I}$V;?l5Ha!)A0EnC8 zkxFz4m_v0SVOj=KhgO+tv`H3aDNU1*9JB<$9X&!VQfR4Acvv~GM)n(S(uwI zP>yu)qu{N@7c=(ay}v($(F*wG)vB`<56yoK{s!jjC8x2~>0l=^EXcj3%3JHRA6a>A<{O0ZEn>X&CKUE{OdnKJ}+`Zes`|-FTNT&dzS)k{lDMd zc?WV5Yl0H2&DvnS^7UXV{5g_#^+EG0^$t}a2F=}nrQC{ZJuTIkhu9uz68^}NcC@rUzlL3$*kyLRbDPqv-Qlsw2;7Lcy_kqz{J1gl7BeDsUk1mm zBBY9_f+9l|h-1ZLd$G%SSA6tvolk!G^;YxNcGsJbS^LQ5 z?5U$!?eFs}j@Xc?pJ6ECEH5!HsG*xEBz{viuj-VI1c!vTQ=-HKh*wf){! zK-YYuV>8i6WY7!-0zq>y5{bq~tYbP;m6YO%ojIM$B*#yjGzSLElPAWLnX%OAXu=x| zurFF(e<0vXrCh5ScZ*P8M1i5=QB$Vrn!MuLhXx`EuPP6AkUo^(3B$b*l(dHoic4Cd z7%pq~TT-F(v7|K$&{vYyf#2j`w(0}oM}1w|gElZIX@#DlF-fb?*^{(}#&AW_I`D6M z+eo8QUPU)K)JiTCOP4ARdP+ewlBQVeXf{Q%$QDh-q8ZTp4aw!R=o}Mb+GsqSqe5-7 zS*X=ZMaxc4S~HgLh&7v=$W7RjS*YyqLNcC*G_vJly-}`hTG=$Z+EJ^+H`LMl3H=!L z^Dqxn=zW^=JN0_)&3d`C(ew$jQe1CBg=^uS#AObu5A!&O1h1eoGTg0QU&6 + + +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 0000000000000000000000000000000000000000..9d03461b653373f7cda3b4af104c6bca07f9892b GIT binary patch literal 1892 zcmcgtO>7%Q7=7dYvttuC>BcFo9o8<6(=@T2jd4&^kqQzA6;LWbX$}ZjQpdJ%;>2-9 zkdO!w5*H3p4_x3>i8B>Q1tD=k2=xS_7m$ofCB&7VLwiBUyq#S)NmC98iCxXk_rBjZ z-_FiL0C)h~P;kCfdj6T!@2-CY7*A3gpP!v8p#YtDgxFemE@v;?`RijqEDIs?@EmH#S?j zPi)rLf1rI1?%Fth|LVxXOVKm85e`rRe7mvr=G{4sv}lVqaS)za#z;-pQ&4s@?Y~s1 zF)-`LxQ;^V&iZa50I>@VLGiR$p>E!vZ{tC{&2B&od7j&{9xM+5=U3(~;w+rxWpS%> z3t7?bxa@oeU5f6^LH6=?gSl=w;=b<-L~c|ZD)Rvmxk8naW2KV2$;*6K?zInfKK$wD zo2{E0-D=X#hu|LQwtx$hrMosccZ~;>{}Qmqf4;bv{)elCcX3t3_yW4S=NPy{XGF1( z6T>G{QK1i*4+%p)W0Q8#G;~8xWrj1ug{k6H(H@Kq{;{L$JDL&H61sljg03gDpz&K` z$j-l!w}%p4VPi+{rJFXS86kCPNevkkZya!OeYxg)xTL3wkC2S_^eBNCzWf@#%K3CmarCGG5fQmm=C5R~V=S)Qo9* zAuoFI(Y|;(sQMc_g7^H7Nbp>U_;LUSQogJpi8WtV>GOdvYY5|WU)HI=;eOd_h?*Yu z*OLS2MZYgAcpRg?tkP%Kmo>z2#g}#Jzs4r&s9_ne0-N>PGN)%zMg^CschDp(S1#2Z z3MDkittICsJ<70PBa1xeX^y>29-iQzV_XiKJ}yG3vDPXznw7F;XYNfv0h$j(Z%)RImyGEQZL6bS0%B+KcnE;+N~vPeDmw~?!W86Kjv>PckT%Q literal 0 HcmV?d00001 diff --git a/components/vc-slick/assets/fonts/slick.woff b/components/vc-slick/assets/fonts/slick.woff new file mode 100644 index 0000000000000000000000000000000000000000..8ee99721bb81b59a5e1ceee1d3256b15f907d96b GIT binary patch literal 1380 zcmZWoc~H_>9DiItQdGPuZL>_xgDjCHpv=4wk37&Nx3m(F$RqMd4ZI>#sTnIvS4$mr zYIRq0kV)NX%VjCA6!RLbG&S?cF0*sBT4Nv8fBW8i=FR8xz30vMSTKtf%;5wBfD-@$ zfCs-1ptSG*j~9z&2>@yZ0O&0cW$QnM8L(KKKmafd=)DGFt!Us*gAdc21vQSALs$X| zzy|;1M1fiYDb!#MKm#b}&(TnX8WVDD zJ{!y|8WSx_7DJ5-xq%i~f8a0TiD5phwg&PQI5s%K6CH#a^aW$E#u1#|iQ5VBN0Z^a z+Wrh7ECB|f#O1CR`r?f}Ogj%bMA#Kog;~-(rbgn@>1o4d$4nYiRyuAWwB_b_E;YM} zZ@NV6aV$`k?$I^#Wy?FR9QXZfCw>r^*1y-k$0lC+Q7Ne`X`$=sB1Wy`n;#2J7g5fr zwyHxDSEMClZ`L6n$e70#EAWLIl3&MaoK(k+l#St%Y&(Sx=VqDv(*x+{1v@swNPOd5 z_D&(udjRFH?3%?ol%y>x6(M%zP2C#?oZgpRVvQB6J2<-s z1XZ2vz<(2aNi{4LTCF&BxGP@+$wvm5GG@1!A5n$t7wEd@)dWbK1t7 zAI3J92@9!?&4dSMUJZ6VPu@Y5=2}-+pk@x!aH)vCoky=Cjr>DnYdb;5B3lEE)C=baaLJ_-dKR1 zWr9+1d0o&|%I}m3VT=Au5?NY}TCUN7+x*?X<*rIq)!7En?lfx*iHingK58Bf7Yt(K z_X`Li%+9B6@V=(#SWT`XQx%ekcKa;n!7{j@jA|tbPOhjX{3cs;}XV-aNF@z-Qg~t z^N?v#oy+p0z}5Q)3S^P$>6P=;iqWo=dqdtaskSzCVSy7fDSNx#E>`??q5MPtvepFdF46=ngEMs$@2`LCj^yh_!*S07+8;<}0 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..42818496918e13cb1a2fb30633983e901986f31f GIT binary patch literal 26139 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d3ab983b797e15aeeff4faa38d672875dbf24395 GIT binary patch literal 10288 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 A&Hw-a literal 0 HcmV?d00001 diff --git a/components/vc-slick/assets/index.less b/components/vc-slick/assets/index.less new file mode 100644 index 000000000..a17c4259e --- /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 000000000..0a6dde662 --- /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 000000000..57477e848 --- /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 000000000..85d8abafd --- /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 000000000..7994a57eb --- /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 000000000..eeca1b97b --- /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 000000000..5ddc6c151 --- /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 000000000..d44b1138d --- /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 000000000..dd762a8a1 --- /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 000000000..95c224d33 --- /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 000000000..46861d078 --- /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 000000000..53773cba9 --- /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 000000000..951a3ec24 --- /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 000000000..75d68f986 --- /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 000000000..3001369af --- /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 000000000..d0483d4f0 --- /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 000000000..60c8a0513 --- /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 000000000..1ddb3ea54 --- /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 000000000..987a5ec46 --- /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 000000000..24167436f --- /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 000000000..95544149e --- /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 000000000..102b65b0a --- /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 000000000..83081568d --- /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 000000000..d65917819 --- /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 000000000..cc328d84e --- /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 000000000..1b77e4335 --- /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 000000000..fa9b5b732 --- /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 000000000..d24905357 --- /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 000000000..bb5f466dc --- /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 000000000..c1a213d0e --- /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 000000000..8bf855a54 --- /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 000000000..c7149d74a --- /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 000000000..8245b7a95 --- /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 000000000..0341872f2 --- /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 000000000..86f2e9e01 --- /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 000000000..d171ce8a8 --- /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 000000000..6abc0720c --- /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 000000000..5ebbc7009 --- /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 000000000..712f4192f --- /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 000000000..434da0eef --- /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 000000000..b052adaba --- /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 000000000..8014d63ae --- /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 000000000..a2fa90ffd --- /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 000000000..92b43a875 --- /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 589a594a6..644bc9c78 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 2bfd38350..b1bc8ad1c 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 5e434fd81..bc53aa35f 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 4c7b8f715..c79eab4e7 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 c17104439..0d84c3138 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 4c6bdbab2..820f34a13 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",