import React, { useMemo } from 'react'
import { TypeGuards } from '@codeleap/types'
import { useCallback } from '@codeleap/hooks'
import { SectionList, SectionListProps as RNSectionProps } from 'react-native'
import { View, ViewProps } from '../View'
import { RefreshControl } from '../RefreshControl'
import { useKeyboardPaddingStyle } from '../../utils'
import { AugmentedSectionRenderItemInfo, SectionComponentProps, SectionProps, SectionRenderComponentProps } from './types'
import { AnyRecord, IJSX, StyledComponentProps } from '@codeleap/styles'
import { MobileStyleRegistry } from '../../Registry'
import { useStylesFor } from '../../hooks'
import { EmptyPlaceholder } from '../EmptyPlaceholder'
export * from './styles'
export * from './types'
const RenderSeparator = (props: { separatorStyles: ViewProps['style'] }) => {
return
}
/**
* Wraps React Native's SectionList. The `sections` data is augmented with a sequential `index`
* field internally — callers must not add their own `index` to section objects or it will be
* overwritten and position helpers (`isFirst`, `isLast`, `isOnly`) will break.
*
* There is no `keyExtractor` default — SectionList falls back to array index, which causes
* re-mount flicker on reorder. Always pass a `keyExtractor` when items can change order or identity.
*/
export function Sections(sectionsProps: SectionProps) {
const {
style,
onRefresh,
refreshing,
placeholder,
refreshControlProps,
loading,
keyboardAware,
fakeEmpty = loading,
contentContainerStyle,
refreshControl,
renderItem: providedRenderItem,
sections: data,
renderSectionHeader: providedRenderSectionHeader,
renderSectionFooter: providedRenderSectionFooter,
...props
} = {
...Sections.defaultProps,
...sectionsProps,
}
/** Deep-equality via JSON.stringify to avoid re-renders on reference changes; will miss non-serialisable values (functions, Dates) in section data. */
const sections = useMemo(() => {
return data?.map((section, index) => ({
...section,
index,
}))
}, [JSON.stringify(data)])
const styles = useStylesFor(Sections.styleRegistryName, style)
const separator = useCallback(() => {
if (!props?.separators) return null
return
}, [])
const getSectionProps = (data: SectionRenderComponentProps) => {
const listLength = sections?.length || 0
const isFirst = data?.section?.index === sections?.[0]?.index
const isLast = data?.section?.index === sections?.[listLength - 1]?.index
const isOnly = isFirst && isLast
const title = data?.section?.title
const index = data?.section?.index
return { isFirst, isLast, isOnly, title, index }
}
const renderSectionHeader = useCallback((data: SectionRenderComponentProps) => {
if (!providedRenderSectionHeader) return null
const positionProps = getSectionProps(data)
return providedRenderSectionHeader({ ...data.section, ...positionProps })
}, [providedRenderSectionHeader, sections?.length])
const renderSectionFooter = useCallback((data: SectionRenderComponentProps) => {
if (!providedRenderSectionFooter) return null
const positionProps = getSectionProps(data)
return providedRenderSectionFooter({ ...data.section, ...positionProps })
}, [providedRenderSectionFooter, sections?.length])
const renderItem = useCallback((data: AugmentedSectionRenderItemInfo) => {
if (!providedRenderItem) return null
const listLength = data?.section?.data?.length || 0
const isFirst = data?.index === 0
const isLast = data?.index === listLength - 1
const isOnly = isFirst && isLast
return providedRenderItem({
...data,
isFirst,
isLast,
isOnly,
})
}, [providedRenderItem])
const isEmpty = !sections || !sections?.length
const _placeholder = {
...placeholder,
loading: TypeGuards.isBoolean(placeholder?.loading) ? placeholder.loading : loading,
}
const keyboardStyle = useKeyboardPaddingStyle([
styles.content,
contentContainerStyle,
isEmpty && styles['content:empty'],
loading && styles['content:loading'],
], keyboardAware && !props.horizontal)
const wrapperStyle = [styles.wrapper, isEmpty && styles['wrapper:empty'], loading && styles['wrapper:loading']]
return (
) : null}
ListEmptyComponent={}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
{...props}
ListHeaderComponentStyle={styles.header}
ListFooterComponentStyle={styles.footer}
style={wrapperStyle}
contentContainerStyle={keyboardStyle}
sections={sections}
renderItem={renderItem}
renderSectionHeader={renderSectionHeader as unknown as RNSectionProps['renderSectionHeader']}
renderSectionFooter={renderSectionFooter as unknown as RNSectionProps['renderSectionHeader']}
/>
)
}
Sections.styleRegistryName = 'Sections'
Sections.elements = ['wrapper', 'content', 'separator', 'header', 'footer', 'refreshControl']
Sections.rootElement = 'wrapper'
Sections.withVariantTypes = (styles: S) => {
return Sections as (props: StyledComponentProps, typeof styles>) => IJSX
}
Sections.defaultProps = {
keyboardShouldPersistTaps: 'handled',
fakeEmpty: false,
loading: false,
keyboardAware: true,
} as Partial
MobileStyleRegistry.registerComponent(Sections)