Skip to content

Commit ff78fdb

Browse files
committed
feat(Swiper): enhance navigation handling and improve configuration checks
1 parent 2542ced commit ff78fdb

File tree

1 file changed

+86
-132
lines changed

1 file changed

+86
-132
lines changed

src/swiper/Swiper.tsx

Lines changed: 86 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2-
import { isNumber } from 'lodash-es';
2+
import { isNumber, isObject } from 'lodash-es';
33
import classNames from 'classnames';
44
import { Property } from 'csstype';
55
import useDefaultProps from '../hooks/useDefaultProps';
@@ -8,18 +8,11 @@ import forwardRefWithStatics from '../_util/forwardRefWithStatics';
88
import { useSwipe } from '../_util/useSwipe';
99
import parseTNode from '../_util/parseTNode';
1010
import { StyledProps } from '../common';
11-
import { SwiperChangeSource, SwiperNavigation, SwiperNavigationType, TdSwiperProps } from './type';
11+
import { SwiperChangeSource, SwiperNavigation, TdSwiperProps } from './type';
1212
import { swiperDefaultProps } from './defaultProps';
1313
import SwiperItem from './SwiperItem';
1414
import SwiperContext, { SwiperItemReference } from './SwiperContext';
1515

16-
const DEFAULT_SWIPER_NAVIGATION: SwiperNavigation = {
17-
paginationPosition: 'bottom',
18-
placement: 'inside',
19-
showControls: false,
20-
type: 'dots',
21-
};
22-
2316
export interface SwiperProps extends TdSwiperProps, StyledProps {
2417
children?: React.ReactNode;
2518
touchable?: boolean;
@@ -30,6 +23,7 @@ enum SwiperStatus {
3023
IDLE = 'idle', // 空闲状态
3124
SWITCHING = 'switching', // 切换状态
3225
STARTDRAG = 'startdrag', // 开始拖拽
26+
ENDDRAG = 'enddrag', // 结束拖拽
3327
}
3428

3529
// swiper组件的动态style
@@ -100,38 +94,43 @@ const Swiper = forwardRefWithStatics(
10094
[props.nextMargin],
10195
);
10296

103-
// 导航器配置
104-
const navigationConfig = useMemo(() => {
105-
if (navigation === true) {
106-
return DEFAULT_SWIPER_NAVIGATION;
107-
}
108-
if (typeof navigation === 'object' && navigation !== null) {
109-
return { ...DEFAULT_SWIPER_NAVIGATION, ...navigation };
110-
}
111-
return {};
112-
}, [navigation]);
113-
11497
// 是否是导航配置
115-
const isSwiperNavigation = useMemo(() => Object.keys(navigationConfig).length > 0, [navigationConfig]);
98+
const isSwiperNavigation = useMemo(() => {
99+
if (!navigation) return false;
100+
const { minShowNum, paginationPosition, placement, showControls, type } = navigation as any;
101+
return (
102+
minShowNum !== undefined ||
103+
paginationPosition !== undefined ||
104+
placement !== undefined ||
105+
showControls !== undefined ||
106+
type !== undefined
107+
);
108+
}, [navigation]);
116109

117110
// 是否显示导航
118111
const enableNavigation = useMemo(() => {
119-
if (!isSwiperNavigation) return false;
120-
const { minShowNum } = navigationConfig;
121-
return minShowNum ? itemCount >= minShowNum : true;
122-
}, [isSwiperNavigation, navigationConfig, itemCount]);
112+
if (isSwiperNavigation) {
113+
const nav = navigation as SwiperNavigation;
114+
return nav?.minShowNum ? itemCount >= nav?.minShowNum : true;
115+
}
116+
return isObject(navigation);
117+
}, [isSwiperNavigation, navigation, itemCount]);
123118

124119
const isBottomPagination = useMemo(() => {
125120
if (!isSwiperNavigation || !enableNavigation) return false;
126-
const { paginationPosition, type } = navigationConfig;
127-
return (!paginationPosition || paginationPosition === 'bottom') && (type === 'dots' || type === 'dots-bar');
128-
}, [enableNavigation, isSwiperNavigation, navigationConfig]);
121+
const nav = navigation as SwiperNavigation;
122+
return (
123+
(!nav?.paginationPosition || nav?.paginationPosition === 'bottom') &&
124+
(nav?.type === 'dots' || nav?.type === 'dots-bar')
125+
);
126+
}, [enableNavigation, isSwiperNavigation, navigation]);
129127

130128
// 导航位置
131129
const navPlacement = useMemo(() => {
132130
if (!isSwiperNavigation) return undefined;
133-
return navigationConfig.placement;
134-
}, [isSwiperNavigation, navigationConfig]);
131+
const nav = navigation as SwiperNavigation;
132+
return nav.placement;
133+
}, [isSwiperNavigation, navigation]);
135134

136135
const rootClass = useMemo(
137136
() => [
@@ -145,7 +144,6 @@ const Swiper = forwardRefWithStatics(
145144

146145
const intervalTimer = useRef<any>(null); // 轮播计时器
147146
const durationTimer = useRef<any>(null); // 轮播动画计时器
148-
const isMounted = useRef(true); // 组件挂载状态
149147
const [itemChange, setItemChange] = useState(false); // 是否处于轮播状态
150148
const [swiperStatus, setSwiperStatus] = useState(SwiperStatus.IDLE); // 轮播状态
151149
const [swiperStyle, setSwiperStyle] = useState<SwiperStyleState>({
@@ -319,14 +317,12 @@ const Swiper = forwardRefWithStatics(
319317
// 进入idle状态
320318
const enterIdle = useCallback((axis: string) => {
321319
navCtrlActive.current = false;
322-
if (isMounted.current) {
323-
setSwiperStyle((prevState) => ({
324-
...prevState,
325-
transition: 'none',
326-
transform: `translate${axis}(0)`,
327-
}));
328-
setSwiperStatus(SwiperStatus.IDLE);
329-
}
320+
setSwiperStyle((prevState) => ({
321+
...prevState,
322+
transition: 'none',
323+
transform: `translate${axis}(0)`,
324+
}));
325+
setSwiperStatus(SwiperStatus.IDLE);
330326
}, []);
331327

332328
// 进入切换状态
@@ -342,13 +338,11 @@ const Swiper = forwardRefWithStatics(
342338
);
343339
updateSwiperItemClassName(index, loop);
344340
setDotIndex(() => index);
345-
if (isMounted.current) {
346-
setSwiperStyle((prevState) => ({
347-
...prevState,
348-
transition: `transform ${duration}ms`,
349-
transform: generateTransform(axis, -step),
350-
}));
351-
}
341+
setSwiperStyle((prevState) => ({
342+
...prevState,
343+
transition: `transform ${duration}ms`,
344+
transform: generateTransform(axis, -step),
345+
}));
352346
setSwiperStatus(SwiperStatus.SWITCHING);
353347
},
354348
[calculateSwiperItemIndex, duration, loop, updateSwiperItemClassName],
@@ -360,36 +354,28 @@ const Swiper = forwardRefWithStatics(
360354
previousIndex.current = calculateItemIndex(nextIndex.current, items.current.length, loop);
361355
updateSwiperItemPosition(axis, previousIndex.current, loop);
362356
enterIdle(axis);
363-
if (isMounted.current) {
364-
setItemChange((prevState) => !prevState);
365-
}
357+
setItemChange((prevState) => !prevState);
366358
},
367359
[calculateItemIndex, enterIdle, loop, updateSwiperItemPosition],
368360
);
369361

370362
// 上一页
371-
const goPrev = useCallback(
372-
(source: SwiperChangeSource) => {
373-
if (disabled) return;
374-
navCtrlActive.current = true;
375-
swiperSource.current = source;
376-
nextIndex.current = previousIndex.current - 1;
377-
enterSwitching(directionAxis, -1);
378-
},
379-
[disabled, enterSwitching, directionAxis],
380-
);
363+
const goPrev = (source: SwiperChangeSource) => {
364+
if (disabled) return;
365+
navCtrlActive.current = true;
366+
swiperSource.current = source;
367+
nextIndex.current = previousIndex.current - 1;
368+
enterSwitching(directionAxis, -1);
369+
};
381370

382371
// 下一页
383-
const goNext = useCallback(
384-
(source: SwiperChangeSource) => {
385-
if (disabled) return;
386-
navCtrlActive.current = true;
387-
swiperSource.current = source;
388-
nextIndex.current = previousIndex.current + 1;
389-
enterSwitching(directionAxis, 1);
390-
},
391-
[disabled, enterSwitching, directionAxis],
392-
);
372+
const goNext = (source: SwiperChangeSource) => {
373+
if (disabled) return;
374+
navCtrlActive.current = true;
375+
swiperSource.current = source;
376+
nextIndex.current = previousIndex.current + 1;
377+
enterSwitching(directionAxis, 1);
378+
};
393379

394380
const onItemClick = () => {
395381
if (disabled) return;
@@ -467,24 +453,7 @@ const Swiper = forwardRefWithStatics(
467453
}
468454
}, [props.height]);
469455

470-
useEffect(
471-
() => () => {
472-
isMounted.current = false;
473-
if (intervalTimer.current) {
474-
clearTimeout(intervalTimer.current);
475-
intervalTimer.current = null;
476-
}
477-
if (durationTimer.current) {
478-
clearTimeout(durationTimer.current);
479-
durationTimer.current = null;
480-
}
481-
},
482-
[],
483-
);
484-
485456
useEffect(() => {
486-
if (!isMounted.current) return;
487-
488457
if (intervalTimer.current) {
489458
clearTimeout(intervalTimer.current);
490459
intervalTimer.current = null;
@@ -510,6 +479,9 @@ const Swiper = forwardRefWithStatics(
510479
case SwiperStatus.STARTDRAG:
511480
nextIndex.current = previousIndex.current;
512481
break;
482+
case SwiperStatus.ENDDRAG:
483+
setSwiperStatus(SwiperStatus.IDLE);
484+
break;
513485
}
514486
}, [autoplay, directionAxis, duration, enterIdle, enterSwitching, interval, quitSwitching, swiperStatus, disabled]);
515487

@@ -546,7 +518,7 @@ const Swiper = forwardRefWithStatics(
546518
[props.height],
547519
);
548520

549-
const swiperNav = useMemo(() => {
521+
const swiperNav = () => {
550522
// 如果 navigation 为 false,不显示导航
551523
if (navigation === false) return null;
552524

@@ -555,25 +527,19 @@ const Swiper = forwardRefWithStatics(
555527
return parseTNode(navigation);
556528
}
557529

558-
// 内置导航器配置
559-
const navType = navigationConfig?.type as SwiperNavigationType | undefined;
560-
const navPaginationPosition = navigationConfig?.paginationPosition as string | undefined;
561-
const navPlacement = navigationConfig?.placement as string | undefined;
562-
const navShowControls = navigationConfig?.showControls as boolean | undefined;
563-
564530
// dots
565-
const dots = () => {
566-
if (navType && ['dots', 'dots-bar'].includes(navType)) {
531+
const dots = (navigation: SwiperNavigation) => {
532+
if (['dots', 'dots-bar'].includes(navigation?.type || '')) {
567533
return (
568534
<>
569535
{items.current.map((_: any, index: number) => (
570536
<span
571537
key={`page${index}`}
572-
className={classNames([
573-
`${swiperNavClass}__${navType}-item`,
574-
index === dotIndex ? `${swiperNavClass}__${navType}-item--active` : '',
575-
`${swiperNavClass}__${navType}-item--${direction}`,
576-
])}
538+
className={classNames(
539+
`${swiperNavClass}__${navigation?.type}-item`,
540+
index === dotIndex ? `${swiperNavClass}__${navigation?.type}-item--active` : '',
541+
`${swiperNavClass}__${navigation?.type}-item--${direction}`,
542+
)}
577543
/>
578544
))}
579545
</>
@@ -582,32 +548,32 @@ const Swiper = forwardRefWithStatics(
582548
};
583549

584550
// fraction
585-
const fraction = () => {
586-
if (navType === 'fraction') {
551+
const fraction = (navigation: SwiperNavigation) => {
552+
if (navigation?.type === 'fraction') {
587553
return <span>{`${(dotIndex ?? 0) + 1}/${items.current.length}`}</span>;
588554
}
589555
};
590556

591-
const typeNav = () => {
592-
if (navType) {
557+
const typeNav = (navigation: SwiperNavigation) => {
558+
if ('type' in navigation) {
593559
return (
594560
<span
595-
className={classNames([
561+
className={classNames(
596562
`${swiperNavClass}--${direction}`,
597-
`${swiperNavClass}__${navType}`,
598-
`${swiperNavClass}--${navPaginationPosition || 'bottom'}`,
599-
`${isBottomPagination && navPlacement ? `${swiperNavClass}--${navPlacement} ` : ''}`,
600-
])}
563+
`${swiperNavClass}__${navigation?.type || ''}`,
564+
`${swiperNavClass}--${navigation?.paginationPosition || 'bottom'}`,
565+
`${isBottomPagination && navigation?.placement ? `${swiperNavClass}--${navigation?.placement} ` : ''}`,
566+
)}
601567
>
602-
{dots()}
603-
{fraction()}
568+
{dots(navigation)}
569+
{fraction(navigation)}
604570
</span>
605571
);
606572
}
607573
};
608574

609-
const controlsNav = () => {
610-
if (!isVertical && navShowControls) {
575+
const controlsNav = (navigation: SwiperNavigation) => {
576+
if (!isVertical && !!navigation?.showControls) {
611577
return (
612578
<span className={`${swiperNavClass}__btn`}>
613579
<span className={`${swiperNavClass}__btn--prev`} onClick={() => goPrev('nav')} />
@@ -617,29 +583,17 @@ const Swiper = forwardRefWithStatics(
617583
}
618584
};
619585

620-
// 如果启用了内置导航器
621-
if (enableNavigation) {
586+
if (!enableNavigation) return '';
587+
if (isSwiperNavigation) {
622588
return (
623589
<>
624-
{controlsNav()}
625-
{typeNav()}
590+
{controlsNav(navigation as SwiperNavigation)}
591+
{typeNav(navigation as SwiperNavigation)}
626592
</>
627593
);
628594
}
629-
630-
return null;
631-
}, [
632-
navigation,
633-
navigationConfig,
634-
enableNavigation,
635-
isBottomPagination,
636-
dotIndex,
637-
direction,
638-
isVertical,
639-
goPrev,
640-
goNext,
641-
swiperNavClass,
642-
]);
595+
return isObject(navigation) ? '' : parseTNode(navigation);
596+
};
643597

644598
return (
645599
<div
@@ -665,7 +619,7 @@ const Swiper = forwardRefWithStatics(
665619
>
666620
<SwiperContext.Provider value={memoProviderValues}>{parseTNode(children)}</SwiperContext.Provider>
667621
</div>
668-
{swiperNav}
622+
{swiperNav()}
669623
</div>
670624
);
671625
},

0 commit comments

Comments
 (0)