Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(pull-down-refresh): update api #479

Merged
merged 11 commits into from
Oct 9, 2024
103 changes: 66 additions & 37 deletions src/pull-down-refresh/PullDownRefresh.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import React, { useRef, useState, ReactNode, useEffect } from 'react';
novlan1 marked this conversation as resolved.
Show resolved Hide resolved
import classNames from 'classnames';
import identity from 'lodash/identity';
import uniqueId from 'lodash/uniqueId';
import { useDrag } from '@use-gesture/react';
import { useSpring, animated } from '@react-spring/web';
anlyyao marked this conversation as resolved.
Show resolved Hide resolved
import isBoolean from 'lodash/isBoolean';
import isNumber from 'lodash/isNumber';
import debounce from 'lodash/debounce';
import { Loading } from 'tdesign-mobile-react';
import useConfig from '../_util/useConfig';
import withNativeProps, { NativeProps } from '../_util/withNativeProps';
novlan1 marked this conversation as resolved.
Show resolved Hide resolved
import getScrollParent from '../_util/getScrollParent';
import delay from '../_util/delay';
import useDefault from '../_util/useDefault';
import { TdPullDownRefreshProps } from './type';
import { pullDownRefreshDefaultProps } from './defaultProps';

const convertUnit = (val: string | number | undefined) => {
if (val == null) return 0;
return isNumber(val) ? `${val}px` : val;
};
novlan1 marked this conversation as resolved.
Show resolved Hide resolved

const reconvertUnit = (val: string | number | undefined) => {
if (val == null) return 0;
return isNumber(val) ? Number(val) : Number(val.slice(0, -2));
};

export enum PullStatusEnum {
normal,
Expand All @@ -37,39 +49,30 @@ function getStatusText(status: PullStatusEnum, loadingTexts: string[]) {

export interface PullDownRefreshProps extends TdPullDownRefreshProps, NativeProps {
disabled?: boolean;
threshold?: number;
onRefresh?: () => Promise<unknown>;
onRefresh?: () => void;
}

const defaultProps = {
loadingBarHeight: 50,
loadingTexts: ['下拉刷新', '松手刷新', '正在刷新', '刷新完成'],
maxBarHeight: 80,
threshold: 50,
refreshTimeout: 3000,
disabled: false,
onRefresh: () => delay(2000),
onTimeout: identity,
};
const threshold = 50;

const PullDownRefresh: React.FC<PullDownRefreshProps> = (props) => {
const {
className,
style,
children,
disabled,
loadingTexts,
loadingProps,
loadingBarHeight,
maxBarHeight,
threshold,
refreshTimeout,
onRefresh,
onTimeout,
value,
onChange,
} = props;
const [status, originalSetStatus] = useState(PullStatusEnum.normal);
const rootRef = useRef<HTMLDivElement>(null);
const scrollParentRef = useRef<Element | Window>(null);
const [value, onChange] = useDefault(props.value, props.defaultValue, props.onChange);
novlan1 marked this conversation as resolved.
Show resolved Hide resolved

const { classPrefix } = useConfig();
novlan1 marked this conversation as resolved.
Show resolved Hide resolved
const name = `${classPrefix}-pull-down-refresh`;
const setStatus = (nextStatus: PullStatusEnum) => {
Expand Down Expand Up @@ -98,14 +101,37 @@ const PullDownRefresh: React.FC<PullDownRefreshProps> = (props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]);

const onReachBottom = debounce(
() => {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 滚动高度
const { clientHeight, scrollHeight } = document.documentElement; // 可视区域/屏幕高度, 页面高度
const distance = 20; // 距离视窗 20 时,开始触发
if (scrollTop + clientHeight >= scrollHeight - distance) {
props.onScrolltolower?.();
}
},
300,
{
leading: true,
trailing: false,
},
);
novlan1 marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
window.addEventListener('scroll', onReachBottom);
return () => {
window.removeEventListener('scroll', onReachBottom);
};
}, [onReachBottom]);

const doRefresh = async () => {
setStatus(PullStatusEnum.loading);
api.start({ y: loadingBarHeight });
api.start({ y: reconvertUnit(loadingBarHeight) });
try {
const timeoutId = uniqueId(`${name}-timeout_`);
let timeoutTid: any;
const res = await Promise.race([
onRefresh(),
onRefresh?.(),
new Promise((resolve) => {
timeoutTid = setTimeout(() => {
resolve(timeoutId);
Expand All @@ -131,7 +157,7 @@ const PullDownRefresh: React.FC<PullDownRefreshProps> = (props) => {
if (!isBoolean(value)) {
doRefresh();
}
onRefresh();
onRefresh?.();
};

useDrag(
Expand All @@ -157,42 +183,45 @@ const PullDownRefresh: React.FC<PullDownRefreshProps> = (props) => {
{
target: rootRef,
from: [0, y.get()],
bounds: { top: 0, bottom: maxBarHeight },
bounds: { top: 0, bottom: reconvertUnit(maxBarHeight) },
pointer: { touch: true },
axis: 'y',
enabled: !disabled && status !== PullStatusEnum.loading,
},
);

const statusText = getStatusText(status, loadingTexts);
let statusNode: ReactNode = statusText;
let statusNode: ReactNode = <div className={`${name}__text`}>{statusText}</div>;
if (status === PullStatusEnum.loading) {
statusNode = (
<Loading
className={`${name}__loading`}
text={<span className={`${name}__loading-icon`}>{statusText}</span>}
{...loadingProps}
/>
);
statusNode = <Loading text={statusText} size="24px" {...loadingProps} />;
}
const loadingHeight = convertUnit(loadingBarHeight);

return withNativeProps(
props,
<div className={name} ref={rootRef}>
<animated.div className={`${name}__track`} style={{ y }}>
<div
className={classNames(`${name}__loading`, `${name}__loading-icon`, `${name}__max`)}
style={{ height: loadingBarHeight }}
>
{statusNode}
<div className={classNames(name, className)} style={style} ref={rootRef}>
<animated.div
className={classNames(`${name}__track`, { [`${name}__track--loosing`]: status !== PullStatusEnum.pulling })}
style={{ y }}
>
<div className={`${name}__tips`}>
<div
className={`${name}__loading`}
style={{
height: loadingHeight,
maxHeight: loadingHeight,
}}
>
{statusNode}
</div>
</div>
{children}
HaixingOoO marked this conversation as resolved.
Show resolved Hide resolved
</animated.div>
</div>,
);
};

PullDownRefresh.defaultProps = defaultProps;
PullDownRefresh.defaultProps = pullDownRefreshDefaultProps;
novlan1 marked this conversation as resolved.
Show resolved Hide resolved
PullDownRefresh.displayName = 'PullDownRefresh';

export default PullDownRefresh;
6 changes: 2 additions & 4 deletions src/pull-down-refresh/_example/base.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ export default function BaseDemo({ children }) {
return (
<div className="tdesign-mobile-wrapper">
<PullDownRefresh
loadingBarHeight={80}
loadingProps={{
layout: 'vertical',
}}
loadingBarHeight={66}
loadingProps={{}}
loadingTexts={['下拉刷新', '松开刷新', '正在刷新', '刷新完成']}
onRefresh={() =>
new Promise((resolve) => {
Expand Down
13 changes: 13 additions & 0 deletions src/pull-down-refresh/defaultProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */

import { TdPullDownRefreshProps } from './type';

export const pullDownRefreshDefaultProps: TdPullDownRefreshProps = {
loadingBarHeight: 50,
loadingTexts: ['下拉刷新', '松手刷新', '正在刷新', '刷新完成'],
maxBarHeight: 80,
refreshTimeout: 3000,
defaultValue: false,
};
22 changes: 22 additions & 0 deletions src/pull-down-refresh/pull-down-refresh.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
:: BASE_DOC ::

## API


### PullDownRefresh Props

name | type | default | description | required
-- | -- | -- | -- | --
className | String | - | className of component | N
style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N
loadingBarHeight | String / Number | 50 | \- | N
loadingProps | Object | - | Typescript:`LoadingProps`,[Loading API Documents](./loading?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/pull-down-refresh/type.ts) | N
loadingTexts | Array | [] | Typescript:`string[]` | N
maxBarHeight | String / Number | 80 | \- | N
refreshTimeout | Number | 3000 | \- | N
value | Boolean | false | \- | N
defaultValue | Boolean | false | uncontrolled property | N
onChange | Function | | Typescript:`(value: boolean) => void`<br/> | N
onRefresh | Function | | Typescript:`() => void`<br/> | N
onScrolltolower | Function | | Typescript:`() => void`<br/> | N
onTimeout | Function | | Typescript:`() => void`<br/> | N
12 changes: 8 additions & 4 deletions src/pull-down-refresh/pull-down-refresh.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@

### PullDownRefresh Props

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
novlan1 marked this conversation as resolved.
Show resolved Hide resolved
className | String | - | 类名 | N
style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
loadingBarHeight | Number | 50 | 加载中下拉高度 | N
loadingProps | Object | - | 加载loading样式。TS 类型:`TdLoadingProps`,[Loading API Documents](./loading?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/pull-down-refresh/type.ts) | N
loadingBarHeight | String / Number | 50 | 加载中下拉高度,如果值为数字则单位是:'px' | N
loadingProps | Object | - | 加载loading样式。TS 类型:`LoadingProps`,[Loading API Documents](./loading?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-react/tree/develop/src/pull-down-refresh/type.ts) | N
loadingTexts | Array | [] | 提示语,组件内部默认值为 ['下拉刷新', '松手刷新', '正在刷新', '刷新完成']。TS 类型:`string[]` | N
maxBarHeight | Number | 80 | 最大下拉高度 | N
maxBarHeight | String / Number | 80 | 最大下拉高度,如果值为数字则单位是:'px' | N
refreshTimeout | Number | 3000 | 刷新超时时间 | N
value | Boolean | false | 组件状态,值为 `true` 表示下拉状态,值为 `false` 表示收起状态 | N
defaultValue | Boolean | false | 组件状态,值为 `true` 表示下拉状态,值为 `false` 表示收起状态。非受控属性 | N
onChange | Function | | TS 类型:`(value: boolean) => void`<br/>下拉或收起时触发,用户手势往下滑动触发下拉状态,手势松开触发收起状态 | N
onRefresh | Function | | TS 类型:`() => void`<br/>结束下拉时触发 | N
onScrolltolower | Function | | TS 类型:`() => void`<br/>滚动到页面底部时触发 | N
onTimeout | Function | | TS 类型:`() => void`<br/>刷新超时触发 | N
2 changes: 1 addition & 1 deletion src/pull-down-refresh/style/index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import '../../_common/style/mobile/components/pull-down-refresh/_index.less';
import '../../_common/style/mobile/components/pull-down-refresh/v2/_index.less';
38 changes: 24 additions & 14 deletions src/pull-down-refresh/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,57 @@
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */

import { TdLoadingProps } from '../loading';
import { LoadingProps } from '../loading';

export interface TdPullDownRefreshProps {
/**
* 加载中下拉高度
* 加载中下拉高度,如果值为数字则单位是:'px'
* @default 50
*/
loadingBarHeight?: number;
loadingBarHeight?: string | number;
/**
* 加载loading样式
*/
loadingProps?: TdLoadingProps;
loadingProps?: LoadingProps;
/**
* 提示语,组件内部默认值为 ['下拉刷新', '松手刷新', '正在刷新', '刷新完成']
* @default []
*/
loadingTexts?: string[];
/**
* 最大下拉高度
* 最大下拉高度,如果值为数字则单位是:'px'
* @default 80
*/
maxBarHeight?: number;
maxBarHeight?: string | number;
/**
* 刷新超时时间
* @default 3000
*/
refreshTimeout?: number;
/**
* 结束下拉时触发
* 组件状态,值为 `true` 表示下拉状态,值为 `false` 表示收起状态
* @default false
*/
onRefresh?: () => void;
value?: boolean;
/**
* 刷新超时触发
* 组件状态,值为 `true` 表示下拉状态,值为 `false` 表示收起状态,非受控属性
* @default false
*/
onTimeout?: () => void;
defaultValue?: boolean;
/**
* 控制是否正在加载
* 下拉或收起时触发,用户手势往下滑动触发下拉状态,手势松开触发收起状态
*/
value?: boolean | undefined;
onChange?: (value: boolean) => void;
/**
* 刷新过程中加载状态的更改
* 结束下拉时触发
*/
onChange?: (value: boolean) => void;
onRefresh?: () => void;
/**
* 滚动到页面底部时触发
*/
onScrolltolower?: () => void;
/**
* 刷新超时触发
*/
onTimeout?: () => void;
}