-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
334 additions
and
0 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,10 @@ | ||
@import '../../styles/var.scss'; | ||
|
||
.demo-list { | ||
padding: 0 0 $padding-md 0; | ||
background-color: $gray-1; | ||
min-height: calc(100vh - 56px); | ||
.pant-cell { | ||
text-align: center; | ||
} | ||
} |
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,121 @@ | ||
import * as preact from 'preact'; | ||
import { Cell } from '../../cell'; | ||
import { Tabs, Tab } from '../../tab'; | ||
import { PullRefresh } from '../../pull-refresh'; | ||
import { List, ListLoadResult } from '../../list'; | ||
import { createBEM } from '../../utils/bem'; | ||
import { NavBar } from '../../_site/scripts/components/nav-bar'; | ||
import './index.scss'; | ||
|
||
const bem = createBEM('demo-list'); | ||
|
||
export class ListRouteComponent extends preact.Component<any, { list1: number[]; list2: number[]; list3: number[] }> { | ||
private list3Ref = preact.createRef<List>(); | ||
|
||
constructor(props: any) { | ||
super(props); | ||
this.state = { | ||
list1: [], | ||
list2: [], | ||
list3: [], | ||
}; | ||
} | ||
|
||
private genList(start: number, end: number): number[] { | ||
const res: number[] = []; | ||
for (let i = start; i < end; i++) { | ||
res.push(i + 1); | ||
} | ||
return res; | ||
} | ||
|
||
render(): preact.JSX.Element { | ||
return ( | ||
<preact.Fragment> | ||
<NavBar title="List" type="list" /> | ||
<div className={bem()}> | ||
<Tabs scrollable> | ||
<Tab title="Basic Usage" lazyRender> | ||
<List | ||
finishedText="Finished" | ||
onLoad={(): Promise<ListLoadResult> => { | ||
return new Promise(resolve => { | ||
setTimeout(() => { | ||
const list1 = this.state.list1; | ||
this.setState({ list1: list1.concat(this.genList(list1.length, list1.length + 10)) }, () => { | ||
resolve({ | ||
finished: this.state.list1.length >= 40, | ||
}); | ||
}); | ||
}, 2000); | ||
}); | ||
}} | ||
> | ||
{this.state.list1.map(i => ( | ||
<Cell title={i}></Cell> | ||
))} | ||
</List> | ||
</Tab> | ||
<Tab title="Error Info" lazyRender> | ||
<List | ||
errorText="Request failed. Click to reload" | ||
onLoad={(): Promise<ListLoadResult> => { | ||
return new Promise(resolve => { | ||
setTimeout(() => { | ||
const list2 = this.state.list2; | ||
this.setState({ list2: list2.concat(this.genList(list2.length, list2.length + 10)) }, () => { | ||
resolve({ | ||
error: this.state.list2.length === 10, | ||
finished: this.state.list2.length >= 40, | ||
}); | ||
}); | ||
}, 2000); | ||
}); | ||
}} | ||
> | ||
{this.state.list2.map(i => ( | ||
<Cell title={i}></Cell> | ||
))} | ||
</List> | ||
</Tab> | ||
<Tab title="PullRefresh" lazyRender> | ||
<PullRefresh | ||
onRefresh={(): Promise<void> => { | ||
return new Promise(resolve => { | ||
setTimeout(() => { | ||
this.setState({ list3: this.genList(0, 10) }, () => { | ||
this.list3Ref.current.check(); | ||
}); | ||
resolve(); | ||
}, 2000); | ||
}); | ||
}} | ||
> | ||
<List | ||
ref={this.list3Ref} | ||
finishedText="Finished" | ||
onLoad={(): Promise<ListLoadResult> => { | ||
return new Promise(resolve => { | ||
setTimeout(() => { | ||
const list3 = this.state.list3; | ||
this.setState({ list3: list3.concat(this.genList(list3.length, list3.length + 10)) }, () => { | ||
resolve({ | ||
finished: this.state.list3.length >= 40, | ||
}); | ||
}); | ||
}, 2000); | ||
}); | ||
}} | ||
> | ||
{this.state.list3.map(i => ( | ||
<Cell title={i}></Cell> | ||
))} | ||
</List> | ||
</PullRefresh> | ||
</Tab> | ||
</Tabs> | ||
</div> | ||
</preact.Fragment> | ||
); | ||
} | ||
} |
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 @@ | ||
@import '../styles/var.scss'; | ||
|
||
.pant-list { | ||
&__loading, | ||
&__finished-text, | ||
&__error-text { | ||
color: $list-text-color; | ||
font-size: $list-text-font-size; | ||
line-height: $list-text-line-height; | ||
text-align: center; | ||
} | ||
|
||
&__placeholder { | ||
height: 0; | ||
pointer-events: none; | ||
} | ||
} |
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,175 @@ | ||
import * as preact from 'preact'; | ||
import { on, off } from '../utils/event'; | ||
import { isHidden } from '../utils/dom'; | ||
import { createBEM } from '../utils/bem'; | ||
import { getScroller, ScrollElement } from '../utils/scroll'; | ||
import { Loading } from '../loading'; | ||
import { i18n } from '../locale'; | ||
import './index.scss'; | ||
|
||
type ListDirection = 'up' | 'down'; | ||
|
||
export type ListLoadResult = { | ||
finished?: boolean; | ||
error?: boolean; | ||
errorText?: string; | ||
errorNode?: preact.VNode; | ||
}; | ||
|
||
export type ListProps = { | ||
direction?: ListDirection; | ||
offset?: number | string; | ||
loadingText?: string; | ||
finishedText?: string; | ||
errorText?: string; | ||
loadingNode?: preact.VNode; | ||
finishedNode?: preact.VNode; | ||
errorNode?: preact.VNode; | ||
onLoad(error?: boolean): Promise<ListLoadResult | void>; | ||
}; | ||
|
||
type ListState = { | ||
loading: boolean; | ||
loadResult: ListLoadResult; | ||
}; | ||
|
||
const bem = createBEM('pant-list'); | ||
|
||
export class List extends preact.Component<ListProps, ListState> { | ||
private containerRef = preact.createRef<HTMLDivElement>(); | ||
private placeholderRef = preact.createRef<HTMLDivElement>(); | ||
private scroller: ScrollElement; | ||
|
||
constructor(props: ListProps) { | ||
super(props); | ||
this.state = { | ||
loading: false, | ||
loadResult: {}, | ||
}; | ||
this.check = this.check.bind(this); | ||
this.onClickError = this.onClickError.bind(this); | ||
} | ||
|
||
componentDidMount(): void { | ||
this.scroller = getScroller(this.containerRef.current); | ||
on(this.scroller, 'scroll', this.check); | ||
this.check(); | ||
} | ||
|
||
componentWillUnmount(): void { | ||
off(this.scroller, 'scroll', this.check); | ||
this.scroller = null; | ||
} | ||
|
||
check(): void { | ||
const { loading, loadResult } = this.state; | ||
if (loading || loadResult.finished || loadResult.error) { | ||
return; | ||
} | ||
|
||
const scroller = this.scroller as any; | ||
const { offset, direction } = this.props; | ||
let scrollerRect; | ||
|
||
if (scroller.getBoundingClientRect) { | ||
scrollerRect = scroller.getBoundingClientRect(); | ||
} else { | ||
scrollerRect = { | ||
top: 0, | ||
bottom: scroller.innerHeight, | ||
}; | ||
} | ||
|
||
const scrollerHeight = scrollerRect.bottom - scrollerRect.top; | ||
|
||
if (!scrollerHeight || isHidden(this.containerRef.current)) { | ||
return; | ||
} | ||
|
||
let isReachEdge = false; | ||
const placeholderRect = this.placeholderRef.current.getBoundingClientRect(); | ||
|
||
if (direction === 'up') { | ||
isReachEdge = scrollerRect.top - placeholderRect.top <= offset; | ||
} else { | ||
isReachEdge = placeholderRect.bottom - scrollerRect.bottom <= offset; | ||
} | ||
|
||
if (isReachEdge) { | ||
this.load(); | ||
} | ||
} | ||
|
||
private load(error?: boolean): void { | ||
this.setState({ loading: true, loadResult: {} }, () => { | ||
this.props.onLoad(error).then(res => { | ||
this.setState({ loading: false, loadResult: res || {} }, () => { | ||
this.check(); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
private genLoading(): preact.JSX.Element { | ||
const { loading, loadResult } = this.state; | ||
const { loadingText, loadingNode } = this.props; | ||
if (loading && !loadResult.finished) { | ||
return ( | ||
<div class={bem('loading')} key="loading"> | ||
{loadingNode || <Loading size="16">{loadingText || i18n().loading}</Loading>} | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
private genFinishedText(): preact.JSX.Element { | ||
const { loadResult } = this.state; | ||
const { finishedText, finishedNode } = this.props; | ||
if (loadResult.finished) { | ||
const text = finishedNode || finishedText; | ||
|
||
if (text) { | ||
return <div class={bem('finished-text')}>{text}</div>; | ||
} | ||
} | ||
} | ||
|
||
private genErrorText(): preact.JSX.Element { | ||
const { loadResult } = this.state; | ||
const { errorText, errorNode } = this.props; | ||
if (loadResult.error) { | ||
const text = loadResult.errorNode || loadResult.errorText || errorNode || errorText; | ||
|
||
if (text) { | ||
return ( | ||
<div onClick={this.onClickError} class={bem('error-text')}> | ||
{text} | ||
</div> | ||
); | ||
} | ||
} | ||
} | ||
|
||
private onClickError(): void { | ||
this.load(true); | ||
} | ||
|
||
render(): preact.JSX.Element { | ||
const { direction, children } = this.props; | ||
const Placeholder = <div ref={this.placeholderRef} class={bem('placeholder')} />; | ||
return ( | ||
<div ref={this.containerRef} class={bem()} role="feed" aria-busy={this.state.loading}> | ||
{direction === 'down' ? children : Placeholder} | ||
{this.genLoading()} | ||
{this.genFinishedText()} | ||
{this.genErrorText()} | ||
{direction === 'up' ? children : Placeholder} | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
List.defaultProps = { | ||
direction: 'down', | ||
offset: 300, | ||
}; |
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