Add toggle to switch between list impl, add default keyExtractor
This commit is contained in:
@@ -26,7 +26,10 @@ export {
|
||||
*
|
||||
* - `contentOffset` - Use `headerOffset` or `footerOffset`
|
||||
*/
|
||||
type ListProps<Item> = Omit<FlatListProps<Item>, 'contentOffset'> & {
|
||||
type ListProps<Item extends {key: string}> = Omit<
|
||||
FlatListProps<Item>,
|
||||
'contentOffset'
|
||||
> & {
|
||||
/**
|
||||
* Wrapper around `onViewableItemsChanged` that calls back with individual
|
||||
* items IF they `item.isViewable` is true.
|
||||
@@ -49,7 +52,7 @@ type ListProps<Item> = Omit<FlatListProps<Item>, 'contentOffset'> & {
|
||||
onScrolledDownChange?: (isScrolledDown: boolean) => void
|
||||
}
|
||||
|
||||
export const List = forwardRef(function List<Item>(
|
||||
export const List = forwardRef(function List<Item extends {key: string}>(
|
||||
props: ListProps<Item>,
|
||||
ref: React.Ref<FlatList<Item>>,
|
||||
) {
|
||||
@@ -126,6 +129,7 @@ export const List = forwardRef(function List<Item>(
|
||||
return (
|
||||
<Animated.FlatList
|
||||
ref={ref}
|
||||
keyExtractor={props.keyExtractor || (i => i.key)}
|
||||
viewabilityConfig={viewabilityConfig}
|
||||
onViewableItemsChanged={onViewableItemsChanged}
|
||||
/**
|
||||
@@ -150,14 +154,6 @@ export const List = forwardRef(function List<Item>(
|
||||
*/
|
||||
right: 1,
|
||||
}}
|
||||
/**
|
||||
* iOS-only, should match `scrollIndicatorInsets`
|
||||
* @see https://reactnative.dev/docs/scrollview#contentinset-ios
|
||||
*/
|
||||
contentInset={{
|
||||
top: props.headerOffset ?? 0,
|
||||
bottom: props.footerOffset ?? 0,
|
||||
}}
|
||||
/**
|
||||
* Native only. On web, we use padding on `style` instead.
|
||||
*/
|
||||
@@ -168,21 +164,21 @@ export const List = forwardRef(function List<Item>(
|
||||
refreshControl={refreshControl}
|
||||
{...(props as FlatListPropsWithLayout<Item>)}
|
||||
style={[
|
||||
/*
|
||||
* On web, the List should always fill its container, otherwise
|
||||
* `onScroll` will not work due to the entire page scrolling.
|
||||
*/
|
||||
{
|
||||
paddingTop: props.headerOffset,
|
||||
paddingBottom: props.footerOffset,
|
||||
},
|
||||
web([
|
||||
/*
|
||||
* On web, the List should always fill its container, otherwise
|
||||
* `onScroll` will not work due to the entire page scrolling.
|
||||
*/
|
||||
a.h_full,
|
||||
{
|
||||
paddingTop: props.headerOffset,
|
||||
paddingBottom: props.footerOffset,
|
||||
},
|
||||
]),
|
||||
]}
|
||||
onScroll={onScroll}
|
||||
/>
|
||||
)
|
||||
}) as <Item>(
|
||||
}) as <Item extends {key: string}>(
|
||||
props: ListProps<Item> & {ref?: React.Ref<FlatList<Item>>},
|
||||
) => React.ReactElement
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {runOnJS} from 'react-native-reanimated'
|
||||
|
||||
import {List as OldList} from '#/view/com/util/List'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {List, ListScrollProvider, useListScrollHandler} from '#/components/List'
|
||||
import {Text} from '#/components/Typography'
|
||||
@@ -36,6 +39,7 @@ type Item = {
|
||||
const log = (msg: any) => console.log(msg)
|
||||
|
||||
export function Inner() {
|
||||
const [old, setOld] = useState<boolean>(false)
|
||||
const onScrollWorklet = useListScrollHandler(e => {
|
||||
'worklet'
|
||||
runOnJS(log)(`scroll ${e.contentOffset.y}`)
|
||||
@@ -43,22 +47,54 @@ export function Inner() {
|
||||
|
||||
return (
|
||||
<View style={[a.h_full_vh]}>
|
||||
<ListScrollProvider onScroll={onScrollWorklet}>
|
||||
<List<Item>
|
||||
data={items}
|
||||
headerOffset={100}
|
||||
footerOffset={100}
|
||||
onScrolledDownChange={scrolledDown => {
|
||||
console.log(`Scrolled down: ${scrolledDown}`)
|
||||
}}
|
||||
renderItem={({item}) => (
|
||||
<View style={[a.p_md, a.border_b]}>
|
||||
<Text>{item.title}</Text>
|
||||
</View>
|
||||
)}
|
||||
style={[a.debug]}
|
||||
/>
|
||||
</ListScrollProvider>
|
||||
<View
|
||||
style={[a.fixed, a.w_full, a.py_sm, a.px_xl, a.align_center, a.z_10]}>
|
||||
<Button
|
||||
label="Toggle Old/New List"
|
||||
onPress={() => setOld(v => !v)}
|
||||
size="tiny"
|
||||
color="primary_subtle">
|
||||
<ButtonText>
|
||||
{old ? 'Testing old List' : 'Testing new List'}
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
{!old ? (
|
||||
<ListScrollProvider onScroll={onScrollWorklet}>
|
||||
<List<Item>
|
||||
data={items}
|
||||
headerOffset={100}
|
||||
footerOffset={100}
|
||||
onScrolledDownChange={scrolledDown => {
|
||||
console.log(`Scrolled down: ${scrolledDown}`)
|
||||
}}
|
||||
renderItem={({item}) => (
|
||||
<View style={[a.p_md, a.border_b]}>
|
||||
<Text>{item.title}</Text>
|
||||
</View>
|
||||
)}
|
||||
style={[a.debug]}
|
||||
/>
|
||||
</ListScrollProvider>
|
||||
) : (
|
||||
<ListScrollProvider onScroll={onScrollWorklet}>
|
||||
<OldList
|
||||
data={items}
|
||||
keyExtractor={item => item.key}
|
||||
headerOffset={100}
|
||||
onScrolledDownChange={scrolledDown => {
|
||||
console.log(`Scrolled down: ${scrolledDown}`)
|
||||
}}
|
||||
renderItem={({item}) => (
|
||||
<View style={[a.p_md, a.border_b]}>
|
||||
<Text>{item.title}</Text>
|
||||
</View>
|
||||
)}
|
||||
style={[a.debug]}
|
||||
/>
|
||||
</ListScrollProvider>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user