forked from Tencent/tdesign-mobile-react
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #68 from jys125773/feature/pull-down-refresh
feat(pull-down-refresh): support new component
- Loading branch information
Showing
16 changed files
with
370 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export default function delay(time: number) { | ||
return new Promise((resolve) => { | ||
setTimeout(resolve, time); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
type ScrollElement = HTMLElement | Window; | ||
|
||
const overflowScrollReg = /scroll|auto|overlay/i; | ||
|
||
export default function getScrollParent( | ||
el: Element | null | undefined, | ||
root: ScrollElement | null | undefined = window, | ||
): Window | Element | null | undefined { | ||
let node = el; | ||
|
||
while (node && node !== root && node.nodeType === 1) { | ||
const { overflowY } = window.getComputedStyle(node); | ||
if (overflowScrollReg.test(overflowY)) { | ||
return node; | ||
} | ||
node = node.parentNode as Element; | ||
} | ||
|
||
return root; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import React, { useRef, useState, ReactNode } from 'react'; | ||
import identity from 'lodash/identity'; | ||
import uniqueId from 'lodash/uniqueId'; | ||
import { useDrag } from '@use-gesture/react'; | ||
import { useSpring, animated } from '@react-spring/web'; | ||
import { Loading } from 'tdesign-mobile-react'; | ||
import useConfig from '../_util/useConfig'; | ||
import withNativeProps, { NativeProps } from '../_util/withNativeProps'; | ||
import getScrollParent from '../_util/getScrollParent'; | ||
import delay from '../_util/delay'; | ||
import { TdPullDownRefreshProps } from './type'; | ||
|
||
export enum PullStatusEnum { | ||
normal, | ||
loading, | ||
loosing, | ||
pulling, | ||
success, | ||
} | ||
|
||
function getStatusText(status: PullStatusEnum, loadingTexts: string[]) { | ||
switch (status) { | ||
case PullStatusEnum.pulling: | ||
return loadingTexts[0]; | ||
case PullStatusEnum.loosing: | ||
return loadingTexts[1]; | ||
case PullStatusEnum.loading: | ||
return loadingTexts[2]; | ||
case PullStatusEnum.success: | ||
return loadingTexts[3]; | ||
default: | ||
return ''; | ||
} | ||
} | ||
|
||
export interface PullDownRefreshProps extends TdPullDownRefreshProps, NativeProps { | ||
disabled?: boolean; | ||
threshold?: number; | ||
onRefresh?: () => Promise<unknown>; | ||
} | ||
|
||
const defaultProps = { | ||
loadingBarHeight: 50, | ||
loadingTexts: ['下拉刷新', '松手刷新', '正在刷新', '刷新完成'], | ||
maxBarHeight: 80, | ||
threshold: 50, | ||
refreshTimeout: 3000, | ||
disabled: false, | ||
onRefresh: () => delay(2000), | ||
onTimeout: identity, | ||
}; | ||
|
||
const PullDownRefresh: React.FC<PullDownRefreshProps> = (props) => { | ||
const { | ||
children, | ||
disabled, | ||
loadingTexts, | ||
loadingProps, | ||
loadingBarHeight, | ||
maxBarHeight, | ||
threshold, | ||
refreshTimeout, | ||
onRefresh, | ||
onTimeout, | ||
} = props; | ||
const [status, originalSetStatus] = useState(PullStatusEnum.normal); | ||
const rootRef = useRef<HTMLDivElement>(null); | ||
const scrollParentRef = useRef<Element | Window>(null); | ||
const { classPrefix } = useConfig(); | ||
const name = `${classPrefix}-pull-down-refresh`; | ||
const setStatus = (nextStatus: PullStatusEnum) => { | ||
if (nextStatus !== status) originalSetStatus(nextStatus); | ||
}; | ||
|
||
const [{ y }, api] = useSpring( | ||
() => ({ | ||
y: 0, | ||
config: { tension: 300, friction: 30, clamp: true }, | ||
}), | ||
[], | ||
); | ||
|
||
const doRefresh = async () => { | ||
setStatus(PullStatusEnum.loading); | ||
api.start({ y: loadingBarHeight }); | ||
try { | ||
const timeoutId = uniqueId(`${name}-timeout_`); | ||
let timeoutTid: any; | ||
const res = await Promise.race([ | ||
onRefresh(), | ||
new Promise((resolve) => { | ||
timeoutTid = setTimeout(() => { | ||
resolve(timeoutId); | ||
onTimeout(); | ||
}, refreshTimeout); | ||
}), | ||
]); | ||
clearTimeout(timeoutTid); | ||
if (res !== timeoutId) { | ||
setStatus(PullStatusEnum.success); | ||
} | ||
} finally { | ||
api.start({ | ||
to: async (next) => { | ||
await next({ y: 0 }); | ||
setStatus(PullStatusEnum.normal); | ||
}, | ||
}); | ||
} | ||
}; | ||
|
||
useDrag( | ||
(state) => { | ||
const [, offsetY] = state.offset; | ||
if (state.first) { | ||
scrollParentRef.current = getScrollParent(rootRef.current); | ||
setStatus(PullStatusEnum.pulling); | ||
} | ||
if (!scrollParentRef.current) return; | ||
if (state.last) { | ||
if (status === PullStatusEnum.loosing) { | ||
doRefresh(); | ||
} else { | ||
setStatus(PullStatusEnum.normal); | ||
api.start({ y: 0 }); | ||
} | ||
} else { | ||
setStatus(offsetY >= threshold ? PullStatusEnum.loosing : PullStatusEnum.pulling); | ||
api.start({ y: offsetY, immediate: true }); | ||
} | ||
}, | ||
{ | ||
target: rootRef, | ||
from: [0, y.get()], | ||
bounds: { top: 0, bottom: maxBarHeight }, | ||
pointer: { touch: true }, | ||
axis: 'y', | ||
enabled: !disabled && status !== PullStatusEnum.loading, | ||
}, | ||
); | ||
|
||
const statusText = getStatusText(status, loadingTexts); | ||
let statusNode: ReactNode = statusText; | ||
if (status === PullStatusEnum.loading) { | ||
statusNode = <Loading {...loadingProps} text={statusText} className={`${name}__loading-icon`} />; | ||
} | ||
|
||
return withNativeProps( | ||
props, | ||
<div className={name} ref={rootRef}> | ||
<animated.div className={`${name}__track`} style={{ y }}> | ||
<div className={`${name}__tips`} style={{ height: loadingBarHeight }}> | ||
{statusNode} | ||
</div> | ||
{children} | ||
</animated.div> | ||
</div>, | ||
); | ||
}; | ||
|
||
PullDownRefresh.defaultProps = defaultProps; | ||
PullDownRefresh.displayName = 'PullDownRefresh'; | ||
|
||
export default PullDownRefresh; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React, { useState } from 'react'; | ||
import { PullDownRefresh } from 'tdesign-mobile-react'; | ||
|
||
export default function Demo() { | ||
const [count, setCount] = useState(0); | ||
return ( | ||
<div> | ||
<PullDownRefresh | ||
onRefresh={() => | ||
new Promise((resolve) => { | ||
setCount(count + 1); | ||
setTimeout(() => { | ||
resolve(); | ||
}, 1000); | ||
}) | ||
} | ||
> | ||
<div className="pull-down-refresh-content">已下拉{count}次</div> | ||
</PullDownRefresh> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from 'react'; | ||
import { Tabs, TabPanel } from 'tdesign-mobile-react/tabs'; | ||
import BaseDemo from './base'; | ||
import TimieoutDemo from './timeout'; | ||
import LoadingTextsDemo from './loading-texts'; | ||
import './style/index.less'; | ||
|
||
export default function Demo() { | ||
return ( | ||
<Tabs> | ||
<TabPanel value={'1'} label="基础用法"> | ||
<BaseDemo /> | ||
</TabPanel> | ||
<TabPanel value={'2'} label="自定义文案"> | ||
<LoadingTextsDemo /> | ||
</TabPanel> | ||
<TabPanel value={'3'} label="超时事件"> | ||
<TimieoutDemo /> | ||
</TabPanel> | ||
</Tabs> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import React, { useState } from 'react'; | ||
import { PullDownRefresh } from 'tdesign-mobile-react'; | ||
|
||
export default function Demo() { | ||
const [count, setCount] = useState(0); | ||
return ( | ||
<div> | ||
<PullDownRefresh | ||
loadingBarHeight={70} | ||
maxBarHeight={100} | ||
loadingTexts={['下拉即可刷新...', '释放即可刷新...', '加载中...', '刷新成功']} | ||
onRefresh={() => | ||
new Promise((resolve) => { | ||
setCount(count + 1); | ||
setTimeout(() => { | ||
resolve(); | ||
}, 1000); | ||
}) | ||
} | ||
> | ||
<div className="pull-down-refresh-content">已下拉{count}次</div> | ||
</PullDownRefresh> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.pull-down-refresh-content { | ||
height: calc(100vh - 55px); | ||
background: #fff; | ||
padding: 20px 16px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React, { useState } from 'react'; | ||
import { PullDownRefresh, Toast } from 'tdesign-mobile-react'; | ||
|
||
export default function Demo() { | ||
const [count, setCount] = useState(0); | ||
return ( | ||
<div> | ||
<PullDownRefresh | ||
refreshTimeout={1000} | ||
onTimeout={() => { | ||
Toast({ message: '已超时' }); | ||
}} | ||
onRefresh={() => | ||
new Promise((resolve) => { | ||
setCount(count + 1); | ||
setTimeout(() => { | ||
resolve(); | ||
}, 2000); | ||
}) | ||
} | ||
> | ||
<div className="pull-down-refresh-content">已下拉{count}次</div> | ||
</PullDownRefresh> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import _PullDownRefresh from './PullDownRefresh'; | ||
import './style'; | ||
|
||
export type { PullDownRefreshProps } from './PullDownRefresh'; | ||
export * from './type'; | ||
|
||
export const PullDownRefresh = _PullDownRefresh; | ||
export default PullDownRefresh; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
:: BASE_DOC :: | ||
|
||
## API | ||
|
||
### PullDownRefresh Props | ||
|
||
名称 | 类型 | 默认值 | 说明 | 必传 | ||
-- | -- | -- | -- | -- | ||
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/TDesignOteam/tdesign-mobile-react/tree/develop/src/pull-down-refresh/type.ts) | N | ||
loadingTexts | Array | [] | 提示语,组件内部默认值为 ['下拉刷新', '松手刷新', '正在刷新', '刷新完成']。TS 类型:`string[]` | N | ||
maxBarHeight | Number | 80 | 最大下拉高度 | N | ||
refreshTimeout | Number | 3000 | 刷新超时时间 | N | ||
onRefresh | Function | | TS 类型:`() => void`<br/>结束下拉时触发 | N | ||
onTimeout | Function | | TS 类型:`() => void`<br/>刷新超时触发 | N |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import './index.css'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import '@common/style/mobile/components/pull-down-refresh/_index.less'; |
Oops, something went wrong.