Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions site/test-coverage.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
module.exports = {
actionSheet: { statements: '100%', branches: '96.55%', functions: '100%', lines: '100%' },
actionSheet: { statements: '98.41%', branches: '89.18%', functions: '100%', lines: '98.36%' },
avatar: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
backTop: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
badge: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
button: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
calendar: { statements: '97.55%', branches: '90%', functions: '98.52%', lines: '98.45%' },
cascader: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
cascader: { statements: '100%', branches: '98.14%', functions: '100%', lines: '100%' },
cell: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
checkbox: { statements: '99.12%', branches: '98.27%', functions: '100%', lines: '100%' },
collapse: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
Expand All @@ -24,11 +24,11 @@ module.exports = {
form: { statements: '2.79%', branches: '0%', functions: '0%', lines: '2.95%' },
grid: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
guide: { statements: '99.43%', branches: '94.49%', functions: '100%', lines: '100%' },
hooks: { statements: '70.7%', branches: '45.88%', functions: '73.68%', lines: '70.66%' },
hooks: { statements: '67.74%', branches: '43%', functions: '71.73%', lines: '67.79%' },
image: { statements: '97.72%', branches: '100%', functions: '92.3%', lines: '97.61%' },
imageViewer: { statements: '8.33%', branches: '2.83%', functions: '0%', lines: '8.69%' },
indexes: { statements: '95.65%', branches: '69.81%', functions: '100%', lines: '96.94%' },
input: { statements: '100%', branches: '98.18%', functions: '100%', lines: '100%' },
indexes: { statements: '96.37%', branches: '77.35%', functions: '100%', lines: '96.94%' },
input: { statements: '100%', branches: '96.49%', functions: '100%', lines: '100%' },
layout: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
link: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
list: { statements: '92%', branches: '77.77%', functions: '100%', lines: '100%' },
Expand All @@ -38,7 +38,7 @@ module.exports = {
navbar: { statements: '100%', branches: '96.15%', functions: '100%', lines: '100%' },
noticeBar: { statements: '6.38%', branches: '0%', functions: '0%', lines: '6.52%' },
overlay: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
picker: { statements: '51.71%', branches: '29.69%', functions: '57.31%', lines: '52.51%' },
picker: { statements: '51.71%', branches: '29.09%', functions: '57.31%', lines: '52.51%' },
popover: { statements: '100%', branches: '96.55%', functions: '100%', lines: '100%' },
popup: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
progress: { statements: '100%', branches: '97.36%', functions: '100%', lines: '100%' },
Expand All @@ -47,19 +47,19 @@ module.exports = {
radio: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
rate: { statements: '99.3%', branches: '98.98%', functions: '100%', lines: '99.3%' },
result: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
search: { statements: '91.83%', branches: '59.09%', functions: '91.66%', lines: '93.75%' },
search: { statements: '82.08%', branches: '55.88%', functions: '66.66%', lines: '83.33%' },
sideBar: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
skeleton: { statements: '100%', branches: '95.83%', functions: '100%', lines: '100%' },
slider: { statements: '97.74%', branches: '96.49%', functions: '100%', lines: '97.68%' },
stepper: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
steps: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
sticky: { statements: '100%', branches: '90%', functions: '100%', lines: '100%' },
swipeCell: { statements: '100%', branches: '98.7%', functions: '100%', lines: '100%' },
swiper: { statements: '57.55%', branches: '37.1%', functions: '67.6%', lines: '59.74%' },
swiper: { statements: '97.29%', branches: '93.87%', functions: '100%', lines: '99.38%' },
switch: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
tabBar: { statements: '100%', branches: '93.18%', functions: '100%', lines: '100%' },
tabBar: { statements: '96.77%', branches: '87.5%', functions: '100%', lines: '96.55%' },
table: { statements: '100%', branches: '90%', functions: '100%', lines: '100%' },
tabs: { statements: '99.35%', branches: '97.5%', functions: '100%', lines: '100%' },
tabs: { statements: '99.35%', branches: '96.34%', functions: '100%', lines: '100%' },
tag: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },
textarea: { statements: '98.64%', branches: '95%', functions: '93.33%', lines: '100%' },
toast: { statements: '98.73%', branches: '100%', functions: '94.11%', lines: '98.66%' },
Expand Down
81 changes: 59 additions & 22 deletions src/swiper/Swiper.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import React, { RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { isNumber, isObject } from 'lodash-es';
import { isNumber } from 'lodash-es';
import classNames from 'classnames';
import { Property } from 'csstype';
import useDefaultProps from '../hooks/useDefaultProps';
import { usePrefixClass } from '../hooks/useClass';
import forwardRefWithStatics from '../_util/forwardRefWithStatics';
import { useSwipe } from '../_util/useSwipe';
import parseTNode from '../_util/parseTNode';
import { StyledProps } from '../common';
import { StyledProps, TNode } from '../common';
import { SwiperChangeSource, SwiperNavigation, TdSwiperProps } from './type';
import { swiperDefaultProps } from './defaultProps';
import SwiperItem from './SwiperItem';
import SwiperContext, { SwiperItemReference } from './SwiperContext';

// 默认导航配置
const DEFAULT_SWIPER_NAVIGATION: SwiperNavigation = {
paginationPosition: 'bottom',
placement: 'inside',
showControls: false,
type: 'dots',
};

export interface SwiperProps extends TdSwiperProps, StyledProps {
children?: React.ReactNode;
touchable?: boolean;
Expand Down Expand Up @@ -96,7 +104,7 @@ const Swiper = forwardRefWithStatics(

// 是否是导航配置
const isSwiperNavigation = useMemo(() => {
if (!navigation) return false;
if (!navigation || typeof navigation === 'boolean') return false;
const { minShowNum, paginationPosition, placement, showControls, type } = navigation as any;
return (
minShowNum !== undefined ||
Expand All @@ -107,30 +115,40 @@ const Swiper = forwardRefWithStatics(
);
}, [navigation]);

// 获取实际使用的导航配置
const getNavigation = useCallback((): SwiperNavigation => {
if (navigation === true) return DEFAULT_SWIPER_NAVIGATION;
if (isSwiperNavigation) return { ...DEFAULT_SWIPER_NAVIGATION, ...(navigation as SwiperNavigation) };
// navigation 是 truthy 但不是有效配置时(如自定义 TNode),返回空对象
return {} as SwiperNavigation;
}, [navigation, isSwiperNavigation]);

// 是否显示导航
const enableNavigation = useMemo(() => {
if (navigation === false) return false;
if (navigation === true) return true;
if (isSwiperNavigation) {
const nav = navigation as SwiperNavigation;
return nav?.minShowNum ? items.current.length > nav?.minShowNum : true;
const nav = getNavigation();
return nav?.minShowNum ? itemCount >= nav?.minShowNum : true;
}
return isObject(navigation);
}, [isSwiperNavigation, navigation]);
return !!navigation;
}, [navigation, isSwiperNavigation, getNavigation, itemCount]);

const isBottomPagination = useMemo(() => {
if (!isSwiperNavigation || !enableNavigation) return false;
const nav = navigation as SwiperNavigation;
const nav = getNavigation();
return (
(!nav?.paginationPosition || nav?.paginationPosition === 'bottom') &&
(nav?.type === 'dots' || nav?.type === 'dots-bar')
);
}, [enableNavigation, isSwiperNavigation, navigation]);
}, [enableNavigation, getNavigation, isSwiperNavigation]);

// 导航位置
const navPlacement = useMemo(() => {
if (!isSwiperNavigation) return undefined;
const nav = navigation as SwiperNavigation;
return nav.placement;
}, [isSwiperNavigation, navigation]);
const nav = getNavigation();
return nav?.placement;
}, [getNavigation, isSwiperNavigation]);

const rootClass = useMemo(
() => [
Expand Down Expand Up @@ -351,16 +369,24 @@ const Swiper = forwardRefWithStatics(
// 退出切换状态
const quitSwitching = useCallback(
(axis: string) => {
previousIndex.current = calculateItemIndex(nextIndex.current, items.current.length, loop);
updateSwiperItemPosition(axis, previousIndex.current, loop);
const newIndex = calculateItemIndex(nextIndex.current, items.current.length, loop);
const wasNavCtrlActive = navCtrlActive.current;
updateSwiperItemPosition(axis, newIndex, loop);
enterIdle(axis);
setItemChange((prevState) => !prevState);
// 导航操作或索引变化时触发 onChange
if (newIndex !== previousIndex.current || wasNavCtrlActive) {
previousIndex.current = newIndex;
setItemChange((prevState) => !prevState);
} else {
previousIndex.current = newIndex;
}
},
[calculateItemIndex, enterIdle, loop, updateSwiperItemPosition],
);

// 上一页
const goPrev = (source: SwiperChangeSource) => {
if (disabled) return;
navCtrlActive.current = true;
swiperSource.current = source;
nextIndex.current = previousIndex.current - 1;
Expand All @@ -369,13 +395,15 @@ const Swiper = forwardRefWithStatics(

// 下一页
const goNext = (source: SwiperChangeSource) => {
if (disabled) return;
navCtrlActive.current = true;
swiperSource.current = source;
nextIndex.current = previousIndex.current + 1;
enterSwitching(directionAxis, 1);
};

const onItemClick = () => {
if (disabled) return;
onClick?.(previousIndex.current ?? 0);
};

Expand Down Expand Up @@ -433,9 +461,10 @@ const Swiper = forwardRefWithStatics(
}, [calculateItemIndex, current, directionAxis, enterSwitching, loop, currentIsNull]);

useEffect(() => {
if (disabled) return;
onChange?.(previousIndex.current, { source: swiperSource.current });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [itemChange]);
}, [itemChange, disabled]);

useEffect(() => {
if (props.height) {
Expand All @@ -458,6 +487,8 @@ const Swiper = forwardRefWithStatics(
clearTimeout(durationTimer.current);
durationTimer.current = null;
}
if (disabled) return;

switch (swiperStatus) {
case SwiperStatus.IDLE:
if (autoplay) {
Expand All @@ -479,7 +510,7 @@ const Swiper = forwardRefWithStatics(
setSwiperStatus(SwiperStatus.IDLE);
break;
}
}, [autoplay, directionAxis, duration, enterIdle, enterSwitching, interval, quitSwitching, swiperStatus]);
}, [autoplay, directionAxis, duration, disabled, enterIdle, enterSwitching, interval, quitSwitching, swiperStatus]);

const changeProvide = () => {
if (props.disabled) return;
Expand Down Expand Up @@ -515,6 +546,11 @@ const Swiper = forwardRefWithStatics(
);

const swiperNav = () => {
// 如果不显示导航,直接返回
if (!enableNavigation) return '';

const nav = getNavigation();

// dots
const dots = (navigation: SwiperNavigation) => {
if (['dots', 'dots-bar'].includes(navigation?.type || '')) {
Expand Down Expand Up @@ -571,16 +607,17 @@ const Swiper = forwardRefWithStatics(
}
};

if (!enableNavigation) return '';
if (isSwiperNavigation) {
if (isSwiperNavigation || navigation === true) {
return (
<>
{controlsNav(navigation as SwiperNavigation)}
{typeNav(navigation as SwiperNavigation)}
{controlsNav(nav)}
{typeNav(nav)}
</>
);
}
return isObject(navigation) ? '' : parseTNode(navigation);
// 对于 TNode 类型(函数、React 元素等),通过 parseTNode 渲染
// 已经排除了 boolean 和 SwiperNavigation,剩余类型断言为 TNode
return parseTNode(navigation as TNode);
};

return (
Expand Down
Loading
Loading