From 08906cb8c2a161bac304c32f4bf4129db97cd1f6 Mon Sep 17 00:00:00 2001 From: eternalsky Date: Tue, 12 Nov 2024 17:07:48 +0800 Subject: [PATCH] fix(Slider): correct typo and improve types definition --- .../slider/__docs__/demo/dots-render/index.md | 2 +- components/slider/__docs__/index.en-us.md | 76 ++++++---- components/slider/__docs__/index.md | 76 ++++++---- components/slider/slick/inner-slider.tsx | 12 +- .../slider/slick/mixins/event-handlers.ts | 119 ++++++++------- components/slider/slick/mixins/helpers.ts | 142 +++++++++++------- components/slider/types.ts | 122 ++++++++++++--- 7 files changed, 344 insertions(+), 205 deletions(-) diff --git a/components/slider/__docs__/demo/dots-render/index.md b/components/slider/__docs__/demo/dots-render/index.md index 7a2cbec3ed..c99d501fc3 100644 --- a/components/slider/__docs__/demo/dots-render/index.md +++ b/components/slider/__docs__/demo/dots-render/index.md @@ -2,7 +2,7 @@ # 自定义导航锚点 -通过 `dotsRender` 可以自定义修改dost,通过 `dotsClass` 可覆盖dots的样式。 +通过 `dotsRender` 可以自定义修改 dots,通过 `dotsClass` 可覆盖 dots 的样式。 # en-US order=6 diff --git a/components/slider/__docs__/index.en-us.md b/components/slider/__docs__/index.en-us.md index 69613359c8..64e134b005 100644 --- a/components/slider/__docs__/index.en-us.md +++ b/components/slider/__docs__/index.en-us.md @@ -26,39 +26,49 @@ The Slider are independent of each other and there is no logical relationship be ### Slider -| Param | Description | Type | Default Value | Required | -| ------------------ | -------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ------------- | -------- | -| className | Custom className | string | - | | -| adaptiveHeight | Whether to use adaptive height | boolean | false | | -| animation | Animation type, default value is 'slide' | string \| boolean | 'slide' | | -| arrows | Whether to display arrows | boolean | true | | -| arrowSize | Size of the arrow | 'medium' \| 'large' | 'medium' | | -| arrowPosition | Position of the arrow | 'inner' \| 'outer' | 'inner' | | -| arrowDirection | Direction of the arrow | 'hoz' \| 'ver' | 'hoz' | | -| autoplay | Whether to play automatically | boolean | false | | -| autoplaySpeed | Autoplay speed | number | 3000 | | -| prevArrow | Prev arrow | ReactElement | - | | -| nextArrow | Next arrow | ReactElement | - | | -| centerMode | Whether to use center mode | boolean | false | | -| dots | Whether to display dots | boolean | true | | -| dotsDirection | Direction for navigation dots | 'hoz' \| 'ver' | 'hoz' | | -| dotsRender | Render navigation dots

**signature**:
**params**:
_index_: index
_current_: current | (index: number, current: number) => void | - | | -| draggable | Whether it can be dragged | boolean | true | | -| infinite | Whether to use infinite loop mode | boolean | true | | -| defaultActiveIndex | The default activated slide index | number | 0 | | -| lazyLoad | Whether to enable lazy load | boolean | false | | -| slideDirection | Slide direction | 'hoz' \| 'ver' | 'hoz' | | -| slidesToShow | Number of slides showed at the same time | number | 1 | | -| slidesToScroll | Number of slides scrolled at the same time | number | 1 | | -| speed | Carousel speed | number | 600 | | -| activeIndex | Jump to the specified carousel image (controlled) | number | - | | -| triggerType | Triggering method for navigation dots | 'click' \| 'hover' | 'click' | | -| onChange | Callback function for slides switching | (index: number) => void | - | | -| centerPadding | Side padding when in center mode (px or %); | string | '50px' | | -| cssEase | CSS3 Animation Easing, default value is 'ease' | string | 'ease' | | -| focusOnSelect | When multiple slides are rotated, whether to be automatically centered after clicking to select them. | boolean | 'false' | | -| style | Custom style | CSSProperties | - | | -| waitForAnimate | Whether to wait for the animation to end before executing the action | boolean | true | | +| Param | Description | Type | Default Value | Required | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------- | -------- | +| className | Custom className | string | - | | +| adaptiveHeight | Whether to use adaptive height | boolean | false | | +| animation | Animation type, default value is 'slide' | string \| boolean | 'slide' | | +| arrows | Whether to display arrows | boolean | true | | +| arrowSize | Size of the arrow | 'medium' \| 'large' | 'medium' | | +| arrowPosition | Position of the arrow | 'inner' \| 'outer' | 'inner' | | +| arrowDirection | Direction of the arrow | 'hoz' \| 'ver' | 'hoz' | | +| autoplay | Whether to play automatically | boolean | false | | +| autoplaySpeed | Autoplay speed | number | 3000 | | +| prevArrow | Prev arrow | ReactElement | - | | +| nextArrow | Next arrow | ReactElement | - | | +| centerMode | Whether to use center mode | boolean | false | | +| dots | Whether to display dots | boolean | true | | +| dotsDirection | Direction for navigation dots | 'hoz' \| 'ver' | 'hoz' | | +| dotsRender | Render navigation dots

**signature**:
**params**:
_index_: index
_current_: current | (index: number, current: number) => void | - | | +| draggable | Whether it can be dragged | boolean | true | | +| infinite | Whether to use infinite loop mode | boolean | true | | +| defaultActiveIndex | The default activated slide index | number | 0 | | +| lazyLoad | Whether to enable lazy load | boolean | false | | +| slideDirection | Slide direction | 'hoz' \| 'ver' | 'hoz' | | +| slidesToShow | Number of slides showed at the same time | number | 1 | | +| slidesToScroll | Number of slides scrolled at the same time | number | 1 | | +| speed | Carousel speed | number | 600 | | +| activeIndex | Jump to the specified carousel image (controlled) | number | - | | +| triggerType | Triggering method for navigation dots | 'click' \| 'hover' | 'click' | | +| onChange | Callback function for slides switching | (index: number) => void | - | | +| centerPadding | Side padding when in center mode (px or %); | string | '50px' | | +| cssEase | CSS3 Animation Easing, default value is 'ease' | string | 'ease' | | +| focusOnSelect | When multiple slides are rotated, whether to be automatically centered after clicking to select them. | boolean | 'false' | | +| style | Custom style | CSSProperties | - | | +| waitForAnimate | Whether to wait for the animation to end before executing the action | boolean | true | | +| accessibility | Whether to enable accessibility support, using the left and right keys to switch the carousel image | boolean | false | | +| children | Children | ReactNode | - | | +| dotsClass | Dots class name | string | - | | +| variableWidth | By default, Slider considers all child elements to be equal. By setting `variableWidth` to `true`, you can place images of different widths in the Slider. | boolean | - | | +| onBeforeChange | Callback function before slides switching | (index: number, currentIndex?: number) => void | - | | +| swipe | Whether to use swipe | boolean | true | | +| edgeEvent | Callback function triggered when sliding to the edge | (swipeDirection: 'left' \| 'right') => void | - | | +| edgeFriction | Edge friction coefficient, the larger the number, the slower the sliding | number | 0.35 | | +| swipeEvent | Callback function for swipe events | (swipeDirection: 'left' \| 'right' \| 'vertical' \| 'down' \| 'up') => void | - | | +| pauseOnHover | Whether to stop the carousel automatically when the mouse hovers | boolean | false | | ## Other diff --git a/components/slider/__docs__/index.md b/components/slider/__docs__/index.md index 1e5b68eb75..919cb99a9a 100644 --- a/components/slider/__docs__/index.md +++ b/components/slider/__docs__/index.md @@ -25,39 +25,49 @@ ### Slider -| 参数 | 说明 | 类型 | 默认值 | 是否必填 | -| ------------------ | --------------------------------------------------------------------------------------------------- | ---------------------------------------- | -------- | -------- | -| className | 自定义传入的样式 | string | - | | -| adaptiveHeight | 是否使用自适应高度 | boolean | false | | -| animation | 动效类型,默认是'slide' | string \| boolean | 'slide' | | -| arrows | 是否显示箭头 | boolean | true | | -| arrowSize | 导航箭头大小 | 'medium' \| 'large' | 'medium' | | -| arrowPosition | 导航箭头位置 | 'inner' \| 'outer' | 'inner' | | -| arrowDirection | 导航箭头方向 | 'hoz' \| 'ver' | 'hoz' | | -| autoplay | 是否自动播放 | boolean | false | | -| autoplaySpeed | 自动播放的速度 | number | 3000 | | -| prevArrow | 前向箭头节点 | ReactElement | - | | -| nextArrow | 后向箭头节点 | ReactElement | - | | -| centerMode | 是否启用居中模式 | boolean | false | | -| dots | 是否显示导航锚点 | boolean | true | | -| dotsDirection | 导航锚点方向 | 'hoz' \| 'ver' | 'hoz' | | -| dotsRender | 自定义导航锚点

**签名**:
**参数**:
_index_: 锚点编号
_current_: 当前幻灯片编号 | (index: number, current: number) => void | - | | -| draggable | 是否可拖拽 | boolean | true | | -| infinite | 是否使用无穷循环模式 | boolean | true | | -| defaultActiveIndex | 初始被激活的轮播图 | number | 0 | | -| lazyLoad | 是否启用懒加载 | boolean | false | | -| slideDirection | 轮播方向 | 'hoz' \| 'ver' | 'hoz' | | -| slidesToShow | 同时展示的图片数量 | number | 1 | | -| slidesToScroll | 同时滑动的图片数量 | number | 1 | | -| speed | 轮播速度 | number | 600 | | -| activeIndex | 跳转到指定的轮播图(受控) | number | - | | -| triggerType | 导航锚点触发方式 | 'click' \| 'hover' | 'click' | | -| onChange | 轮播切换的回调函数 | (index: number) => void | - | | -| centerPadding | center 模式下的边缘 padding 值 (px or %); | string | '50px' | | -| cssEase | CSS3 Animation Easing,默认‘ease’ | string | 'ease' | | -| focusOnSelect | 多图轮播时,是否在点击选中后自动居中 | boolean | 'false' | | -| style | 自定义样式 | CSSProperties | - | | -| waitForAnimate | 是否等待动画结束后再执行动作 | boolean | true | | +| 参数 | 说明 | 类型 | 默认值 | 是否必填 | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -------- | -------- | +| className | 自定义传入的样式 | string | - | | +| adaptiveHeight | 是否使用自适应高度 | boolean | false | | +| animation | 动效类型,默认是'slide' | string \| boolean | 'slide' | | +| arrows | 是否显示箭头 | boolean | true | | +| arrowSize | 导航箭头大小 | 'medium' \| 'large' | 'medium' | | +| arrowPosition | 导航箭头位置 | 'inner' \| 'outer' | 'inner' | | +| arrowDirection | 导航箭头方向 | 'hoz' \| 'ver' | 'hoz' | | +| autoplay | 是否自动播放 | boolean | false | | +| autoplaySpeed | 自动播放的速度 | number | 3000 | | +| prevArrow | 前向箭头节点 | ReactElement | - | | +| nextArrow | 后向箭头节点 | ReactElement | - | | +| centerMode | 是否启用居中模式 | boolean | false | | +| dots | 是否显示导航锚点 | boolean | true | | +| dotsDirection | 导航锚点方向 | 'hoz' \| 'ver' | 'hoz' | | +| dotsRender | 自定义导航锚点

**签名**:
**参数**:
_index_: 锚点编号
_current_: 当前幻灯片编号 | (index: number, current: number) => void | - | | +| draggable | 是否可拖拽 | boolean | true | | +| infinite | 是否使用无穷循环模式 | boolean | true | | +| defaultActiveIndex | 初始被激活的轮播图 | number | 0 | | +| lazyLoad | 是否启用懒加载 | boolean | false | | +| slideDirection | 轮播方向 | 'hoz' \| 'ver' | 'hoz' | | +| slidesToShow | 同时展示的图片数量 | number | 1 | | +| slidesToScroll | 同时滑动的图片数量 | number | 1 | | +| speed | 轮播速度 | number | 600 | | +| activeIndex | 跳转到指定的轮播图(受控) | number | - | | +| triggerType | 导航锚点触发方式 | 'click' \| 'hover' | 'click' | | +| onChange | 轮播切换的回调函数 | (index: number) => void | - | | +| centerPadding | center 模式下的边缘 padding 值 (px or %); | string | '50px' | | +| cssEase | CSS3 Animation Easing,默认‘ease’ | string | 'ease' | | +| focusOnSelect | 多图轮播时,是否在点击选中后自动居中 | boolean | 'false' | | +| style | 自定义样式 | CSSProperties | - | | +| waitForAnimate | 是否等待动画结束后再执行动作 | boolean | true | | +| accessibility | 是否启用无障碍支持,使用左右键可以切换轮播图 | boolean | false | | +| children | 子元素 | ReactNode | - | | +| dotsClass | 导航锚点样式类名 | string | - | | +| variableWidth | Slider 在默认情况下会认为所有的子元素是等宽的。通过设置 `variableWidth` 为 `true`,您可以在 Slider 中放置不同宽度的图片。 | boolean | - | | +| onBeforeChange | 轮播切换前的回调函数 | (index: number, currentIndex?: number) => void | - | | +| swipe | 是否启用滑动 | boolean | true | | +| edgeEvent | 在滑动到头时触发的回调函数 | (swipeDirection: 'left' \| 'right') => void | - | | +| edgeFriction | 边缘摩擦系数,数值越大,滑动越慢 | number | 0.35 | | +| swipeEvent | 滑动事件回调函数 | (swipeDirection: 'left' \| 'right' \| 'vertical' \| 'down' \| 'up') => void | - | | +| pauseOnHover | 在鼠标悬浮时自动停止轮播 | boolean | false | | ## 说明 diff --git a/components/slider/slick/inner-slider.tsx b/components/slider/slick/inner-slider.tsx index 8c319c5b20..4c99cd5a90 100644 --- a/components/slider/slick/inner-slider.tsx +++ b/components/slider/slick/inner-slider.tsx @@ -58,12 +58,12 @@ class InnerSlider extends Component { triggerType: 'click', }; - private hasMounted: boolean = false; - private animationEndCallback: number | undefined; - private pArrow?: HTMLDivElement; - private nArrow?: HTMLDivElement; - private list?: HTMLDivElement; - private track?: HTMLDivElement; + hasMounted: boolean = false; + animationEndCallback: number | undefined; + pArrow?: HTMLDivElement; + nArrow?: HTMLDivElement; + list?: HTMLDivElement; + track?: HTMLDivElement; constructor(props: InnerSliderProps) { super(props); diff --git a/components/slider/slick/mixins/event-handlers.ts b/components/slider/slick/mixins/event-handlers.ts index 03ca9e91fc..2edc97fb95 100644 --- a/components/slider/slick/mixins/event-handlers.ts +++ b/components/slider/slick/mixins/event-handlers.ts @@ -1,26 +1,39 @@ import type { TouchEvent, MouseEvent } from 'react'; import { findDOMNode } from 'react-dom'; import { getTrackCSS, getTrackLeft, getTrackAnimateCSS } from './trackHelper'; -import type { OptionProps } from '../../types'; +import type { InnerSliderProps, InnerSliderState, OptionProps } from '../../types'; +import type { MixinThisType as HelpersMixinThisType } from './helpers'; + +type MixinThisType = { + props: InnerSliderProps; + state: InnerSliderState; + list?: HTMLDivElement; + track?: HTMLDivElement; + changeSlide(options: OptionProps): void; + setState(state: InnerSliderState): void; + getNavigableIndexes(): number[]; + getSlideCount(): number; + checkNavigable(index: number): number; +} & HelpersMixinThisType; const EventHandlers = { // Event handler for previous and next - changeSlide(options: OptionProps) { + changeSlide(this: MixinThisType, options: OptionProps) { let slideOffset, targetSlide; - const unevenOffset = this.state.slideCount % this.props.slidesToScroll !== 0; + const unevenOffset = this.state.slideCount! % this.props.slidesToScroll! !== 0; const indexOffset = unevenOffset ? 0 - : (this.state.slideCount - this.state.currentSlide) % this.props.slidesToScroll; + : (this.state.slideCount! - this.state.currentSlide!) % this.props.slidesToScroll!; if (options.message === 'previous') { slideOffset = indexOffset === 0 ? this.props.slidesToScroll - : this.props.slidesToShow - indexOffset; - targetSlide = this.state.currentSlide - slideOffset; + : this.props.slidesToShow! - indexOffset; + targetSlide = this.state.currentSlide! - slideOffset!; } else if (options.message === 'next') { slideOffset = indexOffset === 0 ? this.props.slidesToScroll : indexOffset; - targetSlide = this.state.currentSlide + slideOffset; + targetSlide = this.state.currentSlide! + slideOffset!; } else if (options.message === 'dots' || options.message === 'children') { // Click on dots targetSlide = options.index! * options.slidesToScroll!; @@ -33,12 +46,12 @@ const EventHandlers = { return; } } - this.slideHandler(targetSlide); + this.slideHandler(targetSlide!); }, - // Accessiblity handler for previous and next - keyHandler(e: KeyboardEvent) { - //Dont slide if the cursor is inside the form fields and arrow keys are pressed + // Accessibility handler for previous and next + keyHandler(this: MixinThisType, e: KeyboardEvent) { + //Don't slide if the cursor is inside the form fields and arrow keys are pressed if (!(e.target as HTMLElement).tagName.match('TEXTAREA|INPUT|SELECT')) { if (e.keyCode === 37 && this.props.accessibility === true) { this.changeSlide({ @@ -53,13 +66,14 @@ const EventHandlers = { }, // Focus on selecting a slide (click handler on track) - selectHandler(options: OptionProps) { + selectHandler(this: MixinThisType, options: OptionProps) { this.changeSlide(options); }, - swipeStart(e: MouseEvent | TouchEvent) { + swipeStart(this: MixinThisType, e: MouseEvent | TouchEvent) { if ( this.props.swipe === false || + // @ts-expect-error 这里逻辑上存在重复 ('ontouchend' in document && this.props.swipe === false) ) { return; @@ -83,17 +97,18 @@ const EventHandlers = { }); }, - swipeMove(e: MouseEvent | TouchEvent) { + swipeMove(this: MixinThisType, e: MouseEvent | TouchEvent) { if (!this.state.dragging) { return; } if (this.state.animating) { return; } - const touchObject = this.state.touchObject; + const touchObject = this.state.touchObject!; const curLeft = getTrackLeft({ - slideIndex: this.state.currentSlide, + slideIndex: this.state.currentSlide!, + // @ts-expect-error 这里拿 ref 的方式是不正确的,应该是 this.track trackRef: this.refs.track, ...this.props, ...this.state, @@ -106,30 +121,30 @@ const EventHandlers = { ? (e as TouchEvent).touches[0].pageY : (e as MouseEvent).clientY; touchObject.swipeLength = Math.round( - Math.sqrt(Math.pow(touchObject.curX - touchObject.startX, 2)) + Math.sqrt(Math.pow(touchObject.curX - touchObject.startX!, 2)) ); let positionOffset = - (this.props.rtl === false ? 1 : -1) * (touchObject.curX > touchObject.startX ? 1 : -1); + (this.props.rtl === false ? 1 : -1) * (touchObject.curX > touchObject.startX! ? 1 : -1); if (this.props.verticalSwiping === true) { touchObject.swipeLength = Math.round( - Math.sqrt(Math.pow(touchObject.curY - touchObject.startY, 2)) + Math.sqrt(Math.pow(touchObject.curY - touchObject.startY!, 2)) ); - positionOffset = touchObject.curY > touchObject.startY ? 1 : -1; + positionOffset = touchObject.curY > touchObject.startY! ? 1 : -1; } const currentSlide = this.state.currentSlide; - const dotCount = Math.ceil(this.state.slideCount / this.props.slidesToScroll); - const swipeDirection = this.swipeDirection(this.state.touchObject); + const dotCount = Math.ceil(this.state.slideCount! / this.props.slidesToScroll!); + const swipeDirection = this.swipeDirection(this.state.touchObject!); let touchSwipeLength = touchObject.swipeLength; if (this.props.infinite === false) { if ( (currentSlide === 0 && swipeDirection === 'right') || - (currentSlide + 1 >= dotCount && swipeDirection === 'left') + (currentSlide! + 1 >= dotCount && swipeDirection === 'left') ) { - touchSwipeLength = touchObject.swipeLength * this.props.edgeFriction; + touchSwipeLength = touchObject.swipeLength * this.props.edgeFriction!; if (this.state.edgeDragged === false && this.props.edgeEvent) { this.props.edgeEvent(swipeDirection); @@ -155,8 +170,8 @@ const EventHandlers = { }); if ( - Math.abs(touchObject.curX - touchObject.startX) < - Math.abs(touchObject.curY - touchObject.startY) * 0.8 + Math.abs(touchObject.curX - touchObject.startX!) < + Math.abs(touchObject.curY - touchObject.startY!) * 0.8 ) { return; } @@ -165,7 +180,7 @@ const EventHandlers = { } }, - getNavigableIndexes() { + getNavigableIndexes(this: MixinThisType) { let max; let breakPoint = 0; let counter = 0; @@ -174,25 +189,25 @@ const EventHandlers = { if (!this.props.infinite) { max = this.state.slideCount; } else { - breakPoint = this.props.slidesToShow * -1; - counter = this.props.slidesToShow * -1; - max = this.state.slideCount * 2; + breakPoint = this.props.slidesToShow! * -1; + counter = this.props.slidesToShow! * -1; + max = this.state.slideCount! * 2; } - while (breakPoint < max) { + while (breakPoint < max!) { indexes.push(breakPoint); - breakPoint = counter + this.props.slidesToScroll; + breakPoint = counter + this.props.slidesToScroll!; counter += - this.props.slidesToScroll <= this.props.slidesToShow - ? this.props.slidesToScroll - : this.props.slidesToShow; + this.props.slidesToScroll! <= this.props.slidesToShow! + ? this.props.slidesToScroll! + : this.props.slidesToShow!; } return indexes; }, - checkNavigable(index: number) { + checkNavigable(this: MixinThisType, index: number) { const navigables = this.getNavigableIndexes(); let prevNavigable = 0; @@ -211,9 +226,9 @@ const EventHandlers = { return index; }, - getSlideCount() { + getSlideCount(this: MixinThisType) { const centerOffset = this.props.centerMode - ? this.state.slideWidth * Math.floor(this.props.slidesToShow / 2) + ? this.state.slideWidth! * Math.floor(this.props.slidesToShow! / 2) : 0; if (this.props.swipeToSlide) { let swipedSlide: HTMLElement | undefined; @@ -227,14 +242,14 @@ const EventHandlers = { if (!this.props.vertical) { if ( slide.offsetLeft - centerOffset + (this.getWidth(slide) || 0) / 2 > - this.state.swipeLeft * -1 + this.state.swipeLeft! * -1 ) { swipedSlide = slide; return false; } } else if ( slide.offsetTop + (this.getHeight(slide) || 0) / 2 > - this.state.swipeLeft * -1 + this.state.swipeLeft! * -1 ) { swipedSlide = slide; return false; @@ -247,23 +262,23 @@ const EventHandlers = { Math.abs(swipedSlide!.dataset!.index! - this.state.currentSlide) || 1; return slidesTraversed; } else { - return this.props.slidesToScroll; + return this.props.slidesToScroll!; } }, - swipeEnd(e: MouseEvent | TouchEvent) { + swipeEnd(this: MixinThisType, e: MouseEvent | TouchEvent) { if (!this.state.dragging) { if (this.props.swipe) { e.preventDefault(); } return; } - const touchObject = this.state.touchObject; - let minSwipe = this.state.listWidth / this.props.touchThreshold; + const touchObject = this.state.touchObject!; + let minSwipe = this.state.listWidth! / this.props.touchThreshold!; const swipeDirection = this.swipeDirection(touchObject); if (this.props.verticalSwiping) { - minSwipe = this.state.listHeight / this.props.touchThreshold; + minSwipe = this.state.listHeight! / this.props.touchThreshold!; } // reset the state of touch related state variables. @@ -288,25 +303,25 @@ const EventHandlers = { switch (swipeDirection) { case 'left': case 'down': - newSlide = this.state.currentSlide + this.getSlideCount(); + newSlide = this.state.currentSlide! + this.getSlideCount(); slideCount = this.props.swipeToSlide ? this.checkNavigable(newSlide) : newSlide; this.setState({ currentDirection: 0 }); break; case 'right': case 'up': - newSlide = this.state.currentSlide - this.getSlideCount(); + newSlide = this.state.currentSlide! - this.getSlideCount(); slideCount = this.props.swipeToSlide ? this.checkNavigable(newSlide) : newSlide; this.setState({ currentDirection: 1 }); break; default: slideCount = this.state.currentSlide; } - this.slideHandler(slideCount); + this.slideHandler(slideCount!); } else { // Adjust the track back to it's original position. const currentLeft = getTrackLeft({ - slideIndex: this.state.currentSlide, - trackRef: this.track, + slideIndex: this.state.currentSlide!, + trackRef: this.track!, ...this.props, ...this.state, }); @@ -321,13 +336,13 @@ const EventHandlers = { } }, - onInnerSliderEnter() { + onInnerSliderEnter(this: MixinThisType) { if (this.props.autoplay && this.props.pauseOnHover) { this.pause(); } }, - onInnerSliderLeave() { + onInnerSliderLeave(this: MixinThisType) { if (this.props.autoplay && this.props.pauseOnHover) { this.autoPlay(); } diff --git a/components/slider/slick/mixins/helpers.ts b/components/slider/slick/mixins/helpers.ts index 648eac4164..d0b3267daf 100644 --- a/components/slider/slick/mixins/helpers.ts +++ b/components/slider/slick/mixins/helpers.ts @@ -1,14 +1,35 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { getTrackCSS, getTrackLeft, getTrackAnimateCSS } from './trackHelper'; -import type { InnerSliderProps } from '../../types'; +import type { InnerSliderProps, InnerSliderState } from '../../types'; + +export type MixinThisType = { + props: InnerSliderProps; + state: InnerSliderState; + list?: HTMLDivElement; + track?: HTMLDivElement; + animationEndCallback: number | undefined; + hasMounted: boolean; + getWidth(elem: Element): number; + getHeight(elem: Element): number; + setState(state: InnerSliderState, cb?: () => void): void; + autoPlay(): void; + initialize(props: InnerSliderProps): void; + canGoNext(opts: InnerSliderProps): boolean; + slideHandler(index: number): void; + play(): false | void; + pause(): void; + swipeDirection( + touchObject: InnerSliderState['touchObject'] + ): 'left' | 'right' | 'vertical' | 'down' | 'up'; +}; const helpers = { - initialize(props: InnerSliderProps) { + initialize(this: MixinThisType, props: InnerSliderProps) { const slickList = ReactDOM.findDOMNode(this.list) as Element; const slideCount = React.Children.count(props.children); const listWidth = this.getWidth(slickList) || 0; - const trackWidth = this.getWidth(ReactDOM.findDOMNode(this.track)) || 0; + const trackWidth = this.getWidth(ReactDOM.findDOMNode(this.track) as Element) || 0; let slideWidth; if (!props.vertical) { @@ -19,7 +40,7 @@ const helpers = { slideWidth = listWidth; } - const slideHeight = this.getHeight(slickList.querySelector('[data-index="0"]')) || 0; + const slideHeight = this.getHeight(slickList.querySelector('[data-index="0"]')!) || 0; const listHeight = slideHeight * props.slidesToShow!; const slideHeightList = []; const newSlickList = Array.from( @@ -49,8 +70,8 @@ const helpers = { }, () => { const targetLeft = getTrackLeft({ - slideIndex: this.state.currentSlide, - trackRef: this.track, + slideIndex: this.state.currentSlide!, + trackRef: this.track!, ...props, ...this.state, }); @@ -68,7 +89,7 @@ const helpers = { ); }, - update(props: InnerSliderProps) { + update(this: MixinThisType, props: InnerSliderProps) { this.initialize(props); }, @@ -86,7 +107,7 @@ const helpers = { return elem && (elem as Element).getBoundingClientRect().height; }, - adaptHeight() { + adaptHeight(this: MixinThisType) { if (this.props.adaptiveHeight) { const selector = `[data-index="${this.state.currentSlide}"]`; if (this.list) { @@ -98,7 +119,7 @@ const helpers = { } }, - canGoNext(opts: InnerSliderProps) { + canGoNext(opts: InnerSliderProps & InnerSliderState) { let canGo = true; if (!opts.infinite) { if (opts.centerMode) { @@ -116,7 +137,7 @@ const helpers = { return canGo; }, - slideHandler(index: number) { + slideHandler(this: MixinThisType, index: number) { const { rtl } = this.props; // Functionality of animateSlide and postSlide is merged into this function @@ -128,25 +149,25 @@ const helpers = { } if (this.props.animation === 'fade') { - currentSlide = this.state.currentSlide; + currentSlide = this.state.currentSlide!; // don't change slide if it's not infinite and current slide is the first or last slide' - if (this.props.infinite === false && (index < 0 || index >= this.state.slideCount)) { + if (this.props.infinite === false && (index < 0 || index >= this.state.slideCount!)) { return; } // Shifting targetSlide back into the range if (index < 0) { - targetSlide = index + this.state.slideCount; - } else if (index >= this.state.slideCount) { - targetSlide = index - this.state.slideCount; + targetSlide = index + this.state.slideCount!; + } else if (index >= this.state.slideCount!) { + targetSlide = index - this.state.slideCount!; } else { targetSlide = index; } - if (this.props.lazyLoad && this.state.lazyLoadedList.indexOf(targetSlide) < 0) { + if (this.props.lazyLoad && this.state.lazyLoadedList!.indexOf(targetSlide) < 0) { this.setState({ - lazyLoadedList: this.state.lazyLoadedList.concat(targetSlide), + lazyLoadedList: this.state.lazyLoadedList!.concat(targetSlide), }); } @@ -154,11 +175,11 @@ const helpers = { this.setState({ animating: false, }); - this.props.onChange(targetSlide); + this.props.onChange!(targetSlide); delete this.animationEndCallback; }; - this.props.onBeforeChange(this.state.currentSlide, targetSlide); + this.props.onBeforeChange!(this.state.currentSlide!, targetSlide); this.setState( { @@ -180,23 +201,23 @@ const helpers = { if (targetSlide < 0) { if (this.props.infinite === false) { currentSlide = 0; - } else if (this.state.slideCount % this.props.slidesToScroll !== 0) { - if (targetSlide + this.props.slidesToScroll <= 0) { - currentSlide = this.state.slideCount + targetSlide; - targetSlide = this.state.slideCount - this.props.slidesToScroll; + } else if (this.state.slideCount! % this.props.slidesToScroll! !== 0) { + if (targetSlide + this.props.slidesToScroll! <= 0) { + currentSlide = this.state.slideCount! + targetSlide; + targetSlide = this.state.slideCount! - this.props.slidesToScroll!; } else { currentSlide = targetSlide = 0; } } else { - currentSlide = this.state.slideCount + targetSlide; + currentSlide = this.state.slideCount! + targetSlide; } - } else if (targetSlide >= this.state.slideCount) { + } else if (targetSlide >= this.state.slideCount!) { if (this.props.infinite === false) { - currentSlide = this.state.slideCount - this.props.slidesToShow; - } else if (this.state.slideCount % this.props.slidesToScroll !== 0) { + currentSlide = this.state.slideCount! - this.props.slidesToShow!; + } else if (this.state.slideCount! % this.props.slidesToScroll! !== 0) { currentSlide = 0; } else { - currentSlide = targetSlide - this.state.slideCount; + currentSlide = targetSlide - this.state.slideCount!; } } else { currentSlide = targetSlide; @@ -204,19 +225,19 @@ const helpers = { } else if (targetSlide < 0) { if (this.props.infinite === false) { currentSlide = 0; - } else if (this.state.slideCount % this.props.slidesToScroll !== 0) { + } else if (this.state.slideCount! % this.props.slidesToScroll! !== 0) { currentSlide = - this.state.slideCount - (this.state.slideCount % this.props.slidesToScroll); + this.state.slideCount! - (this.state.slideCount! % this.props.slidesToScroll!); } else { - currentSlide = this.state.slideCount + targetSlide; + currentSlide = this.state.slideCount! + targetSlide; } - } else if (targetSlide >= this.state.slideCount) { + } else if (targetSlide >= this.state.slideCount!) { if (this.props.infinite === false) { - currentSlide = this.state.slideCount - this.props.slidesToShow; - } else if (this.state.slideCount % this.props.slidesToScroll !== 0) { + currentSlide = this.state.slideCount! - this.props.slidesToShow!; + } else if (this.state.slideCount! % this.props.slidesToScroll! !== 0) { currentSlide = 0; } else { - currentSlide = targetSlide - this.state.slideCount; + currentSlide = targetSlide - this.state.slideCount!; } } else { currentSlide = targetSlide; @@ -224,14 +245,14 @@ const helpers = { let targetLeft = getTrackLeft({ slideIndex: targetSlide, - trackRef: this.track, + trackRef: this.track!, ...this.props, ...this.state, }); const currentLeft = getTrackLeft({ slideIndex: currentSlide, - trackRef: this.track, + trackRef: this.track!, ...this.props, ...this.state, }); @@ -243,11 +264,11 @@ const helpers = { if (this.props.lazyLoad) { let loaded = true; const slidesToLoad = []; - const slidesLen = this.state.slideCount; + const slidesLen = this.state.slideCount!; const sliderIndex = targetSlide < 0 ? slidesLen + targetSlide : currentSlide; - for (let i = sliderIndex; i < sliderIndex + this.props.slidesToShow; i++) { + for (let i = sliderIndex; i < sliderIndex + this.props.slidesToShow!; i++) { let k = i; if (rtl) { k = i >= slidesLen ? slidesLen * 2 - i - 1 : slidesLen - i - 1; @@ -256,25 +277,25 @@ const helpers = { const pre = k - 1 < 0 ? slidesLen - 1 : k - 1; const next = k + 1 >= slidesLen ? 0 : k + 1; - this.state.lazyLoadedList.indexOf(k) < 0 && slidesToLoad.push(k); - this.state.lazyLoadedList.indexOf(pre) < 0 && slidesToLoad.push(pre); - this.state.lazyLoadedList.indexOf(next) < 0 && slidesToLoad.push(next); + this.state.lazyLoadedList!.indexOf(k) < 0 && slidesToLoad.push(k); + this.state.lazyLoadedList!.indexOf(pre) < 0 && slidesToLoad.push(pre); + this.state.lazyLoadedList!.indexOf(next) < 0 && slidesToLoad.push(next); } slidesToLoad.forEach(i => { - if (this.state.lazyLoadedList.indexOf(i) < 0) { + if (this.state.lazyLoadedList!.indexOf(i) < 0) { loaded = false; } }); if (!loaded) { this.setState({ - lazyLoadedList: this.state.lazyLoadedList.concat(slidesToLoad), + lazyLoadedList: this.state.lazyLoadedList!.concat(slidesToLoad), }); } } - this.props.onBeforeChange(this.state.currentSlide, currentSlide); + this.props.onBeforeChange!(this.state.currentSlide!, currentSlide); // Slide Transition happens here. // animated transition happens to target Slide and @@ -291,7 +312,7 @@ const helpers = { }), }, () => { - this.props.onChange(currentSlide); + this.props.onChange!(currentSlide); } ); } else { @@ -308,7 +329,7 @@ const helpers = { callback = () => { this.setState(nextStateChanges); - this.props.onChange(currentSlide); + this.props.onChange!(currentSlide); delete this.animationEndCallback; }; @@ -332,11 +353,11 @@ const helpers = { }, // 鼠标悬浮在 arrow 上时作出动画反馈 - arrowHoverHandler(msg?: string) { + arrowHoverHandler(this: MixinThisType, msg?: string) { const offset = 30; // slide 的位置偏移量 const targetLeft = getTrackLeft({ - slideIndex: this.state.currentSlide, - trackRef: this.track, + slideIndex: this.state.currentSlide!, + trackRef: this.track!, ...this.props, ...this.state, }); @@ -360,10 +381,15 @@ const helpers = { }); }, - swipeDirection(touchObject: { startX: number; startY: number; curX: number; curY: number }) { + swipeDirection(touchObject: { + startX?: number; + startY?: number; + curX?: number; + curY?: number; + }) { let swipeAngle; - const xDist = touchObject.startX - touchObject.curX; - const yDist = touchObject.startY - touchObject.curY; + 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) { @@ -385,22 +411,22 @@ const helpers = { return 'vertical'; }, - play() { + play(this: MixinThisType) { let nextIndex; if (!this.hasMounted) { return false; } if (this.props.rtl) { - nextIndex = this.state.currentSlide - this.props.slidesToScroll; + nextIndex = this.state.currentSlide! - this.props.slidesToScroll!; } else if (this.canGoNext({ ...this.props, ...this.state })) { - nextIndex = this.state.currentSlide + this.props.slidesToScroll; + nextIndex = this.state.currentSlide! + this.props.slidesToScroll!; } else { return false; } this.slideHandler(nextIndex); }, - autoPlay() { + autoPlay(this: MixinThisType) { if (this.state.autoPlayTimer) { clearTimeout(this.state.autoPlayTimer); } @@ -411,7 +437,7 @@ const helpers = { } }, - pause() { + pause(this: MixinThisType) { if (this.state.autoPlayTimer) { clearTimeout(this.state.autoPlayTimer); this.setState({ diff --git a/components/slider/types.ts b/components/slider/types.ts index fe3e96abe8..37b7384ebc 100644 --- a/components/slider/types.ts +++ b/components/slider/types.ts @@ -79,24 +79,22 @@ export interface ArrowProps } export interface InnerSliderProps - extends Omit, - Omit, - DotsProps, - Omit, - Omit { - arrows?: SliderProps['arrows']; - dots?: SliderProps['dots']; - defaultActiveIndex?: SliderProps['defaultActiveIndex']; - centerPadding?: SliderProps['centerPadding']; - focusOnSelect?: SliderProps['focusOnSelect']; + extends Omit< + SliderProps, + 'slideDirection' | 'style' | 'className' | 'vertical' | 'verticalSwiping' + > { + /** + * 是否启用垂直轮播 + * @en Whether to use vertical carousel + */ + vertical?: boolean; + /** + * 是否启用垂直滑动 + * @en Whether to use vertical sliding + */ verticalSwiping?: boolean; - adaptiveHeight?: SliderProps['adaptiveHeight']; - onChange?: SliderProps['onChange']; - onBeforeChange?: (index: number, currentIndex?: number) => void; - children?: ReactNode; - prevArrow?: SliderProps['prevArrow']; - nextArrow?: SliderProps['nextArrow']; } + export interface InnerSliderState { lazyLoadedList?: number[]; slideCount?: number | null; @@ -115,10 +113,11 @@ export interface InnerSliderState { slideHeightList?: number[]; swipeLeft?: number | null; touchObject?: { - startX: number; - startY: number; - curX: number; - curY: number; + startX?: number; + startY?: number; + curX?: number; + curY?: number; + swipeLength?: number; }; initialized?: boolean; edgeDragged?: boolean; @@ -132,8 +131,7 @@ export interface InnerSliderState { */ export interface SliderProps extends Omit, - Omit, - InnerSliderProps { + Omit { /** * 自定义传入的样式 * @en Custom className @@ -374,4 +372,84 @@ export interface SliderProps * @skip */ beforeChange?: (index: number) => void; + /** + * 是否启用无障碍支持,使用左右键可以切换轮播图 + * @en Whether to enable accessibility support, using the left and right keys to switch the carousel image + * @defaultValue false + */ + accessibility?: boolean; + /** + * 子元素 + * @en Children + */ + children?: ReactNode; + /** + * 导航锚点样式类名 + * @en Dots class name + */ + dotsClass?: string; + /** + * Slider 在默认情况下会认为所有的子元素是等宽的。通过设置 `variableWidth` 为 `true`,您可以在 Slider 中放置不同宽度的图片。 + * @en By default, Slider considers all child elements to be equal. By setting `variableWidth` to `true`, you can place images of different widths in the Slider. + */ + variableWidth?: boolean; + /** + * 是否启用垂直轮播 + * @en Whether to use vertical carousel + * @deprecated use slideDirection=ver instead + * @skip + */ + vertical?: boolean; + /** + * 是否启用垂直滑动 + * @en Whether to use vertical sliding + * @deprecated use slideDirection=ver instead + * @skip + */ + verticalSwiping?: boolean; + /** + * 轮播切换前的回调函数 + * @en Callback function before slides switching + */ + onBeforeChange?: (index: number, currentIndex?: number) => void; + /** + * 是否启用滑动 + * @en Whether to use swipe + * @defaultValue true + */ + swipe?: boolean; + /** + * 在滑动到头时触发的回调函数 + * @en Callback function triggered when sliding to the edge + */ + edgeEvent?: (swipeDirection: 'left' | 'right') => void; + /** + * 边缘摩擦系数,数值越大,滑动越慢 + * @en Edge friction coefficient, the larger the number, the slower the sliding + * @defaultValue 0.35 + */ + edgeFriction?: number; + /** + * 滑动事件回调函数 + * @en Callback function for swipe events + */ + swipeEvent?: (swipeDirection: 'left' | 'right' | 'vertical' | 'down' | 'up') => void; + /** + * @skip + */ + touchThreshold?: number; + /** + * @skip + */ + swipeToSlide?: boolean; + /** + * 在鼠标悬浮时自动停止轮播 + * @en Whether to stop the carousel automatically when the mouse hovers + * @defaultValue false + */ + pauseOnHover?: boolean; + /** + * @skip + */ + useCSS?: boolean; }