Further align web List
with FlatList
, add contain
mode to web list implementation (#3867)
* add `onStartReached` to web list * fix `rootMargin` * Add `contain`, handle scroll events * improve types, fix typo * simplify * adjust `scrollToTop` and `scrollToOffset` to support `contain`, add `scrollToEnd` * rename `handleWindowScroll` to `handleScroll` * support basic `maintainVisibleContentPosition` * rename `contain` to `containWeb` * remove unnecessary `flex: 1` * add missing props * add root prop to `Visibility` * add root prop to `Visibility` * revert adding `maintainVisibleContentPosition` * oops * always apply `flex: 1` to styles when contained * add a contained list to storybook * make `onScroll` a worklet in storybook * revert test code * add scrolling to storybook * simplify getting scrollable node * nit: extra whitespace * nit: random comment * foolproof the logic * typecheck
This commit is contained in:
parent
594b40c3ae
commit
bc07019911
4 changed files with 316 additions and 91 deletions
98
src/view/screens/Storybook/ListContained.tsx
Normal file
98
src/view/screens/Storybook/ListContained.tsx
Normal file
|
@ -0,0 +1,98 @@
|
|||
import React from 'react'
|
||||
import {FlatList, View} from 'react-native'
|
||||
|
||||
import {ScrollProvider} from 'lib/ScrollContext'
|
||||
import {List} from 'view/com/util/List'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function ListContained() {
|
||||
const [animated, setAnimated] = React.useState(false)
|
||||
const ref = React.useRef<FlatList>(null)
|
||||
|
||||
const data = React.useMemo(() => {
|
||||
return Array.from({length: 100}, (_, i) => ({
|
||||
id: i,
|
||||
text: `Message ${i}`,
|
||||
}))
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={{width: '100%', height: 300}}>
|
||||
<ScrollProvider
|
||||
onScroll={() => {
|
||||
'worklet'
|
||||
console.log('onScroll')
|
||||
}}>
|
||||
<List
|
||||
data={data}
|
||||
renderItem={item => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
padding: 10,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: 'rgba(0,0,0,0.1)',
|
||||
}}>
|
||||
<Text>{item.item.text}</Text>
|
||||
</View>
|
||||
)
|
||||
}}
|
||||
keyExtractor={item => item.id.toString()}
|
||||
containWeb={true}
|
||||
style={{flex: 1}}
|
||||
onStartReached={() => {
|
||||
console.log('Start Reached')
|
||||
}}
|
||||
onEndReached={() => {
|
||||
console.log('End Reached (threshold of 2)')
|
||||
}}
|
||||
onEndReachedThreshold={2}
|
||||
ref={ref}
|
||||
disableVirtualization={true}
|
||||
/>
|
||||
</ScrollProvider>
|
||||
</View>
|
||||
|
||||
<View style={{flexDirection: 'row', gap: 10, alignItems: 'center'}}>
|
||||
<Toggle.Item
|
||||
name="a"
|
||||
label="Click me"
|
||||
value={animated}
|
||||
onChange={() => setAnimated(prev => !prev)}>
|
||||
<Toggle.Checkbox />
|
||||
<Toggle.LabelText>Animated Scrolling</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="large"
|
||||
label="Scroll to End"
|
||||
onPress={() => ref.current?.scrollToOffset({animated, offset: 0})}>
|
||||
<ButtonText>Scroll to Top</ButtonText>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="large"
|
||||
label="Scroll to End"
|
||||
onPress={() => ref.current?.scrollToEnd({animated})}>
|
||||
<ButtonText>Scroll to End</ButtonText>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="large"
|
||||
label="Scroll to Offset 100"
|
||||
onPress={() => ref.current?.scrollToOffset({animated, offset: 500})}>
|
||||
<ButtonText>Scroll to Offset 500</ButtonText>
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {ScrollView, View} from 'react-native'
|
||||
|
||||
import {useSetThemePrefs} from '#/state/shell'
|
||||
import {CenteredView, ScrollView} from '#/view/com/util/Views'
|
||||
import {isWeb} from 'platform/detection'
|
||||
import {CenteredView} from '#/view/com/util/Views'
|
||||
import {ListContained} from 'view/screens/Storybook/ListContained'
|
||||
import {atoms as a, ThemeProvider, useTheme} from '#/alf'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {Breakpoints} from './Breakpoints'
|
||||
|
@ -18,77 +20,111 @@ import {Theming} from './Theming'
|
|||
import {Typography} from './Typography'
|
||||
|
||||
export function Storybook() {
|
||||
const t = useTheme()
|
||||
const {setColorMode, setDarkTheme} = useSetThemePrefs()
|
||||
if (isWeb) return <StorybookInner />
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<CenteredView style={[t.atoms.bg]}>
|
||||
<View style={[a.p_xl, a.gap_5xl, {paddingBottom: 200}]}>
|
||||
<View style={[a.flex_row, a.align_start, a.gap_md]}>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="primary"
|
||||
size="small"
|
||||
label='Set theme to "system"'
|
||||
onPress={() => setColorMode('system')}>
|
||||
<ButtonText>System</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "light"'
|
||||
onPress={() => setColorMode('light')}>
|
||||
<ButtonText>Light</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "dim"'
|
||||
onPress={() => {
|
||||
setColorMode('dark')
|
||||
setDarkTheme('dim')
|
||||
}}>
|
||||
<ButtonText>Dim</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "dark"'
|
||||
onPress={() => {
|
||||
setColorMode('dark')
|
||||
setDarkTheme('dark')
|
||||
}}>
|
||||
<ButtonText>Dark</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
<Dialogs />
|
||||
<ThemeProvider theme="light">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
<ThemeProvider theme="dim">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
<ThemeProvider theme="dark">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
|
||||
<Typography />
|
||||
<Spacing />
|
||||
<Shadows />
|
||||
<Buttons />
|
||||
<Icons />
|
||||
<Links />
|
||||
<Forms />
|
||||
<Dialogs />
|
||||
<Menus />
|
||||
<Breakpoints />
|
||||
</View>
|
||||
</CenteredView>
|
||||
<StorybookInner />
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
|
||||
function StorybookInner() {
|
||||
const t = useTheme()
|
||||
const {setColorMode, setDarkTheme} = useSetThemePrefs()
|
||||
const [showContainedList, setShowContainedList] = React.useState(false)
|
||||
|
||||
return (
|
||||
<CenteredView style={[t.atoms.bg]}>
|
||||
<View style={[a.p_xl, a.gap_5xl, {paddingBottom: 200}]}>
|
||||
{!showContainedList ? (
|
||||
<>
|
||||
<View style={[a.flex_row, a.align_start, a.gap_md]}>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="primary"
|
||||
size="small"
|
||||
label='Set theme to "system"'
|
||||
onPress={() => setColorMode('system')}>
|
||||
<ButtonText>System</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "light"'
|
||||
onPress={() => setColorMode('light')}>
|
||||
<ButtonText>Light</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "dim"'
|
||||
onPress={() => {
|
||||
setColorMode('dark')
|
||||
setDarkTheme('dim')
|
||||
}}>
|
||||
<ButtonText>Dim</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="small"
|
||||
label='Set theme to "dark"'
|
||||
onPress={() => {
|
||||
setColorMode('dark')
|
||||
setDarkTheme('dark')
|
||||
}}>
|
||||
<ButtonText>Dark</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
<Dialogs />
|
||||
<ThemeProvider theme="light">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
<ThemeProvider theme="dim">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
<ThemeProvider theme="dark">
|
||||
<Theming />
|
||||
</ThemeProvider>
|
||||
|
||||
<Typography />
|
||||
<Spacing />
|
||||
<Shadows />
|
||||
<Buttons />
|
||||
<Icons />
|
||||
<Links />
|
||||
<Forms />
|
||||
<Dialogs />
|
||||
<Menus />
|
||||
<Breakpoints />
|
||||
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="large"
|
||||
label="Switch to Contained List"
|
||||
onPress={() => setShowContainedList(true)}>
|
||||
<ButtonText>Switch to Contained List</ButtonText>
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="large"
|
||||
label="Switch to Storybook"
|
||||
onPress={() => setShowContainedList(false)}>
|
||||
<ButtonText>Switch to Storybook</ButtonText>
|
||||
</Button>
|
||||
<ListContained />
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</CenteredView>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue