@codeleap/mobile 4.2.5 → 4.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/components/SectionFilters/context.d.ts +46 -0
  2. package/dist/components/SectionFilters/context.js +10 -0
  3. package/dist/components/SectionFilters/context.js.map +1 -0
  4. package/dist/components/SectionFilters/index.d.ts +10 -0
  5. package/dist/components/SectionFilters/index.js +84 -0
  6. package/dist/components/SectionFilters/index.js.map +1 -0
  7. package/dist/components/SectionFilters/types.d.ts +19 -0
  8. package/dist/components/SectionFilters/types.js +3 -0
  9. package/dist/components/SectionFilters/types.js.map +1 -0
  10. package/dist/components/SectionFilters/useSectionFilters.d.ts +35 -0
  11. package/dist/components/SectionFilters/useSectionFilters.js +133 -0
  12. package/dist/components/SectionFilters/useSectionFilters.js.map +1 -0
  13. package/dist/components/Sections/index.d.ts +11 -3
  14. package/dist/components/Sections/index.js +54 -38
  15. package/dist/components/Sections/index.js.map +1 -1
  16. package/dist/components/Sections/styles.d.ts +5 -1
  17. package/dist/components/Sections/types.d.ts +26 -12
  18. package/dist/components/components.d.ts +1 -0
  19. package/dist/components/components.js +1 -0
  20. package/dist/components/components.js.map +1 -1
  21. package/package.json +5 -5
  22. package/package.json.bak +1 -1
  23. package/src/components/SectionFilters/context.tsx +15 -0
  24. package/src/components/SectionFilters/index.tsx +80 -0
  25. package/src/components/SectionFilters/types.ts +29 -0
  26. package/src/components/SectionFilters/useSectionFilters.tsx +166 -0
  27. package/src/components/Sections/index.tsx +99 -57
  28. package/src/components/Sections/styles.ts +6 -1
  29. package/src/components/Sections/types.ts +33 -16
  30. package/src/components/components.ts +1 -0
@@ -40,3 +40,4 @@ export * from './SearchInput';
40
40
  export * from './PaginationIndicator';
41
41
  export * from './PlacesAutocomplete';
42
42
  export * from './SortablePhotos';
43
+ export * from './SectionFilters';
@@ -56,4 +56,5 @@ __exportStar(require("./SearchInput"), exports);
56
56
  __exportStar(require("./PaginationIndicator"), exports);
57
57
  __exportStar(require("./PlacesAutocomplete"), exports);
58
58
  __exportStar(require("./SortablePhotos"), exports);
59
+ __exportStar(require("./SectionFilters"), exports);
59
60
  //# sourceMappingURL=components.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/components/components.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAsB;AACtB,yCAAsB;AACtB,8CAA2B;AAC3B,yCAAsB;AACtB,0CAAuB;AACvB,6CAA0B;AAC1B,8CAA2B;AAC3B,+CAA4B;AAC5B,2CAAwB;AACxB,6CAA0B;AAC1B,2CAAwB;AACxB,8CAA2B;AAC3B,2CAAwB;AACxB,qDAAkC;AAClC,+CAA4B;AAC5B,2CAAwB;AACxB,yCAAsB;AACtB,sDAAmC;AACnC,2CAAwB;AACxB,0CAAuB;AACvB,6CAA0B;AAC1B,8CAA2B;AAC3B,+CAA4B;AAC5B,0CAAuB;AACvB,qDAAkC;AAClC,6CAA0B;AAC1B,qDAAkC;AAClC,iDAA8B;AAC9B,yCAAsB;AACtB,mDAAgC;AAChC,+CAA4B;AAC5B,8CAA2B;AAC3B,oDAAiC;AACjC,mDAAgC;AAChC,2CAAwB;AACxB,gDAA6B;AAC7B,oDAAiC;AACjC,0CAAuB;AACvB,gDAA6B;AAC7B,wDAAqC;AACrC,uDAAoC;AACpC,mDAAgC"}
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/components/components.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAsB;AACtB,yCAAsB;AACtB,8CAA2B;AAC3B,yCAAsB;AACtB,0CAAuB;AACvB,6CAA0B;AAC1B,8CAA2B;AAC3B,+CAA4B;AAC5B,2CAAwB;AACxB,6CAA0B;AAC1B,2CAAwB;AACxB,8CAA2B;AAC3B,2CAAwB;AACxB,qDAAkC;AAClC,+CAA4B;AAC5B,2CAAwB;AACxB,yCAAsB;AACtB,sDAAmC;AACnC,2CAAwB;AACxB,0CAAuB;AACvB,6CAA0B;AAC1B,8CAA2B;AAC3B,+CAA4B;AAC5B,0CAAuB;AACvB,qDAAkC;AAClC,6CAA0B;AAC1B,qDAAkC;AAClC,iDAA8B;AAC9B,yCAAsB;AACtB,mDAAgC;AAChC,+CAA4B;AAC5B,8CAA2B;AAC3B,oDAAiC;AACjC,mDAAgC;AAChC,2CAAwB;AACxB,gDAA6B;AAC7B,oDAAiC;AACjC,0CAAuB;AACvB,gDAA6B;AAC7B,wDAAqC;AACrC,uDAAoC;AACpC,mDAAgC;AAChC,mDAAgC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/mobile",
3
- "version": "4.2.5",
3
+ "version": "4.2.6",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -9,16 +9,16 @@
9
9
  "directory": "packages/mobile"
10
10
  },
11
11
  "devDependencies": {
12
- "@codeleap/common": "4.2.5",
13
- "@codeleap/config": "4.2.5"
12
+ "@codeleap/common": "4.2.6",
13
+ "@codeleap/config": "4.2.6"
14
14
  },
15
15
  "scripts": {
16
16
  "build": "tsc --build",
17
17
  "lint": "eslint -c .eslintrc.js --fix \"./src/**/*.{ts,tsx,js,jsx}\""
18
18
  },
19
19
  "peerDependencies": {
20
- "@codeleap/common": "4.2.5",
21
- "@codeleap/styles": "4.2.5",
20
+ "@codeleap/common": "4.2.6",
21
+ "@codeleap/styles": "4.2.6",
22
22
  "@d11/react-native-fast-image": "8.8.0",
23
23
  "@react-native-firebase/messaging": "14.4.0",
24
24
  "@react-navigation/bottom-tabs": "6.5.3",
package/package.json.bak CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/mobile",
3
- "version": "4.2.5",
3
+ "version": "4.2.6",
4
4
  "main": "src/index.ts",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -0,0 +1,15 @@
1
+ import { createContext, useContext } from 'react'
2
+ import { SectionFiltersProps } from './types'
3
+ import { TSectionFilterItem, useSectionFilters } from './useSectionFilters'
4
+
5
+ export type SectionFiltersContextProps<T = TSectionFilterItem> = React.PropsWithChildren<SectionFiltersProps<T> & {
6
+ handle?: ReturnType<typeof useSectionFilters<T>>
7
+ }>
8
+
9
+ type TSectionFiltersContext<T = TSectionFilterItem> = ReturnType<typeof useSectionFilters<T>>
10
+
11
+ export const SectionsFilterContext = createContext({} as TSectionFiltersContext)
12
+
13
+ export function useSectionFiltersContext<T = TSectionFilterItem>() {
14
+ return useContext(SectionsFilterContext) as TSectionFiltersContext<T>
15
+ }
@@ -0,0 +1,80 @@
1
+ import { memoBy, TypeGuards } from '@codeleap/common'
2
+ import { Button } from '../Button'
3
+ import { AugmentedSectionRenderItemInfo, SectionComponentProps, Sections } from '../Sections'
4
+ import { Text } from '../Text'
5
+ import { SectionsFilterContext, useSectionFiltersContext } from './context'
6
+ import { SectionFilterComponentProps, SectionFiltersProps } from './types'
7
+ import { TSectionFilterItem, useSectionFilters } from './useSectionFilters'
8
+
9
+ export * from './types'
10
+ export * from './context'
11
+ export * from './useSectionFilters'
12
+
13
+ const SectionComponent = memoBy((props: SectionComponentProps<TSectionFilterItem> & { renderWith: (props: SectionFilterComponentProps<TSectionFilterItem>) => JSX.Element }) => {
14
+ const { renderWith: Component, index } = props
15
+
16
+ const handle = useSectionFiltersContext()
17
+
18
+ if (!Component) return null
19
+
20
+ return (
21
+ <Component
22
+ {...props}
23
+ selectedItems={handle.selectedItems[index] ?? []}
24
+ clearSelectedItems={() => handle.clearSelectedItemsWithSection(index)}
25
+ />
26
+ )
27
+ }, ['renderWith'])
28
+
29
+ export function SectionFilters<T extends TSectionFilterItem = TSectionFilterItem>(props: SectionFiltersProps<T>) {
30
+ const {
31
+ sections,
32
+ renderItem: RenderItem,
33
+ renderSectionHeader,
34
+ renderSectionFooter,
35
+ ...rest
36
+ } = {
37
+ ...SectionFilters.defaultProps,
38
+ ...props,
39
+ }
40
+
41
+ const handle = props.handle ?? useSectionFilters(props)
42
+
43
+ const renderItem = (section: AugmentedSectionRenderItemInfo<T>) => {
44
+ const hasSection = !TypeGuards.isNil(section?.index) && !TypeGuards.isNil(section)
45
+ const sectionLimitReached = hasSection && handle.sectionLimitReached(section?.index)
46
+ const limitReached = handle.limitReached()
47
+ const disableOnReachLimit = handle.disableItemsOnLimitReached
48
+ const disableNonSelectedItems = (limitReached || sectionLimitReached) && disableOnReachLimit
49
+
50
+ const isSelected = handle.isSelected(section?.item)
51
+ const isDisabled = disableNonSelectedItems && !isSelected
52
+
53
+ return (
54
+ <RenderItem
55
+ {...section}
56
+ onPress={() => handle.toggleItem(section?.item)}
57
+ selected={isSelected}
58
+ disabled={isDisabled}
59
+ text={section?.item?.label}
60
+ />
61
+ )
62
+ }
63
+
64
+ return (
65
+ <SectionsFilterContext.Provider value={handle}>
66
+ <Sections
67
+ {...rest}
68
+ sections={sections}
69
+ renderItem={renderItem}
70
+ renderSectionHeader={(props) => <SectionComponent {...props} renderWith={renderSectionHeader} />}
71
+ renderSectionFooter={(props) => <SectionComponent {...props} renderWith={renderSectionFooter} />}
72
+ />
73
+ </SectionsFilterContext.Provider>
74
+ )
75
+ }
76
+
77
+ SectionFilters.defaultProps = {
78
+ renderItem: Button as unknown,
79
+ renderSectionHeader: ({ title }) => <Text text={title} />
80
+ } as Partial<SectionFiltersProps>
@@ -0,0 +1,29 @@
1
+ import { ReactElement } from 'react'
2
+ import { AugmentedSectionRenderItemInfo, SectionComponentProps, SectionProps } from '../Sections'
3
+ import { TSectionFilterItem, UseSectionFilters, useSectionFilters } from './useSectionFilters'
4
+
5
+ export type SectionFilterItemProps<T> =
6
+ AugmentedSectionRenderItemInfo<T> &
7
+ {
8
+ onPress: () => void
9
+ selected: boolean
10
+ disabled: boolean
11
+ text: string
12
+ }
13
+
14
+ export type SectionFilterComponentProps<T> =
15
+ SectionComponentProps<T> &
16
+ {
17
+ selectedItems: TSectionFilterItem[]
18
+ clearSelectedItems: () => void
19
+ }
20
+
21
+ export type SectionFiltersProps<T = any> =
22
+ UseSectionFilters &
23
+ Omit<SectionProps<T>, 'renderItem' | 'renderSectionFooter' | 'renderSectionHeader'> &
24
+ {
25
+ handle?: ReturnType<typeof useSectionFilters<T>>
26
+ renderItem?: (props: SectionFilterItemProps<T>) => ReactElement
27
+ renderSectionFooter?: (props: SectionFilterComponentProps<T>) => ReactElement
28
+ renderSectionHeader?: (props: SectionFilterComponentProps<T>) => ReactElement
29
+ }
@@ -0,0 +1,166 @@
1
+ import { deepEqual, TypeGuards } from '@codeleap/common'
2
+ import React from 'react'
3
+
4
+ export type TSectionFilterItem = {
5
+ value?: string | number
6
+ label?: string
7
+ }
8
+
9
+ type Section<T = TSectionFilterItem> = {
10
+ data: T[]
11
+ title: string
12
+ selectionLimit?: number
13
+ disableItemsOnLimitReached?: boolean
14
+ }
15
+
16
+ type SelectedItemsPerSection<T = TSectionFilterItem> = { [X: number]: T[] }
17
+
18
+ export type UseSectionFilters<T = TSectionFilterItem> = {
19
+ sections: Section<T>[]
20
+ areItemsEqual?: (a: T, b: T) => boolean
21
+ selectionLimit?: number
22
+ sectionSelectionLimit?: number
23
+ disableItemsOnLimitReached?: boolean
24
+ initialSelectedItems?: SelectedItemsPerSection<T>
25
+ }
26
+
27
+ export function useSectionFilters<T = TSectionFilterItem>(props: UseSectionFilters<T>) {
28
+ const {
29
+ sections,
30
+ areItemsEqual = deepEqual,
31
+ selectionLimit = 1,
32
+ sectionSelectionLimit = null,
33
+ disableItemsOnLimitReached = selectionLimit > 1 && !sectionSelectionLimit,
34
+ initialSelectedItems = [],
35
+ } = props
36
+
37
+ const [selectedItems, setSelectedItems] = React.useState<SelectedItemsPerSection<T>>(() => {
38
+ if (TypeGuards.isArray(initialSelectedItems)) {
39
+ return {
40
+ 0: initialSelectedItems,
41
+ }
42
+ }
43
+
44
+ return initialSelectedItems ?? {}
45
+ })
46
+
47
+ const findItemSection = (item: T) => {
48
+ if (!sections) {
49
+ return {
50
+ sectionIndex: 0,
51
+ section: null,
52
+ }
53
+ }
54
+
55
+ const sectionIndex = sections?.findIndex((section) => {
56
+ return section.data.some((i) => areItemsEqual(item, i))
57
+ })
58
+
59
+ if (sectionIndex === -1) {
60
+ return {
61
+ sectionIndex: null,
62
+ section: null,
63
+ }
64
+ }
65
+
66
+ const section = sections[sectionIndex]
67
+
68
+ return {
69
+ sectionIndex,
70
+ section,
71
+ }
72
+ }
73
+
74
+ const isSelected = (item: T) => {
75
+ if (sections) {
76
+ const { sectionIndex } = findItemSection(item)
77
+
78
+ return selectedItems[sectionIndex]?.some((i) => areItemsEqual(i, item))
79
+ }
80
+
81
+ return selectedItems[0]?.some((i) => areItemsEqual(i, item))
82
+ }
83
+
84
+ const toggleItem = (item: T) => {
85
+ let sectionIndex = -1
86
+ let limit = selectionLimit
87
+
88
+ if (sections) {
89
+ const { sectionIndex: si, section } = findItemSection(item)
90
+
91
+ if (si === null) {
92
+ return
93
+ }
94
+
95
+ sectionIndex = si
96
+ limit = section.selectionLimit ?? selectionLimit
97
+ } else {
98
+ sectionIndex = 0
99
+ }
100
+
101
+ const currentItems = selectedItems[sectionIndex] ?? []
102
+
103
+ const isItemSelected = currentItems.some((i) => areItemsEqual(i, item))
104
+
105
+ const newItems = [...currentItems]
106
+
107
+ if (isItemSelected) {
108
+ const index = newItems.findIndex((i) => areItemsEqual(i, item))
109
+
110
+ newItems.splice(index, 1)
111
+ } else {
112
+ if (newItems.length >= limit) {
113
+ newItems.shift()
114
+ }
115
+
116
+ newItems.push(item)
117
+ }
118
+
119
+ setSelectedItems({
120
+ ...selectedItems,
121
+ [sectionIndex]: newItems,
122
+ })
123
+ }
124
+
125
+ function sectionLimitReached(sectionIndex: number) {
126
+ const section = sections[sectionIndex]
127
+
128
+ if (!section) {
129
+ return false
130
+ }
131
+
132
+ const limit = section.selectionLimit ?? sectionSelectionLimit ?? selectionLimit
133
+
134
+ if (!limit) {
135
+ return false
136
+ }
137
+
138
+ const nItems = selectedItems[sectionIndex]?.length
139
+
140
+ return nItems >= limit
141
+ }
142
+
143
+ function limitReached() {
144
+ const nItems = Object.values(selectedItems).flatMap((i) => i).length
145
+
146
+ return nItems >= selectionLimit
147
+ }
148
+
149
+ function clearSelectedItemsWithSection(sectionIndex: number) {
150
+ setSelectedItems({
151
+ ...selectedItems,
152
+ [sectionIndex]: [],
153
+ })
154
+ }
155
+
156
+ return {
157
+ isSelected,
158
+ toggleItem,
159
+ findItemSection,
160
+ selectedItems,
161
+ sectionLimitReached,
162
+ limitReached,
163
+ disableItemsOnLimitReached,
164
+ clearSelectedItemsWithSection,
165
+ }
166
+ }
@@ -1,118 +1,160 @@
1
- import React, { forwardRef } from 'react'
2
- import { useCallback } from '@codeleap/common'
3
- import { RefreshControl, SectionList } from 'react-native'
4
- import { View } from '../View'
1
+ import React, { useMemo } from 'react'
2
+ import { TypeGuards, useCallback } from '@codeleap/common'
3
+ import { SectionList, SectionListProps as RNSectionProps } from 'react-native'
4
+ import { View, ViewProps } from '../View'
5
+ import { RefreshControl } from '../RefreshControl'
5
6
  import { useKeyboardPaddingStyle } from '../../utils'
6
- import { SectionListProps } from './types'
7
- import { AnyRecord, IJSX, StyledComponentProps, StyledComponentWithProps } from '@codeleap/styles'
7
+ import { AugmentedSectionRenderItemInfo, SectionComponentProps, SectionProps, SectionRenderComponentProps } from './types'
8
+ import { AnyRecord, IJSX, StyledComponentProps } from '@codeleap/styles'
8
9
  import { MobileStyleRegistry } from '../../Registry'
9
10
  import { useStylesFor } from '../../hooks'
11
+ import { EmptyPlaceholder } from '../EmptyPlaceholder'
10
12
 
11
13
  export * from './styles'
12
14
  export * from './types'
13
15
 
14
- export const Sections = forwardRef<SectionList, SectionListProps>((sectionsProps, ref) => {
16
+ const RenderSeparator = (props: { separatorStyles: ViewProps['style'] }) => {
17
+ return <View style={props.separatorStyles} />
18
+ }
19
+
20
+ export const Sections = <T extends any>(sectionsProps: SectionProps<T>) => {
15
21
  const {
16
22
  style,
17
23
  onRefresh,
18
24
  component,
19
25
  refreshing,
20
26
  placeholder,
21
- keyboardAware,
22
27
  refreshControlProps,
28
+ loading,
29
+ keyboardAware,
30
+ fakeEmpty = loading,
23
31
  contentContainerStyle,
24
- fakeEmpty,
25
32
  refreshControl,
33
+ renderItem: RenderItem,
34
+ sections: data,
35
+ renderSectionHeader: RenderSectionHeader,
36
+ renderSectionFooter: RenderSectionFooter,
26
37
  ...props
27
38
  } = {
28
39
  ...Sections.defaultProps,
29
40
  ...sectionsProps,
30
41
  }
31
42
 
43
+ const sections = useMemo(() => {
44
+ return data?.map((section, index) => ({
45
+ ...section,
46
+ index,
47
+ }))
48
+ }, [JSON.stringify(data)])
49
+
32
50
  const styles = useStylesFor(Sections.styleRegistryName, style)
33
51
 
34
- const renderSeparator = useCallback(() => {
35
- return <View style={styles?.separator} />
36
- }, [styles?.separator])
52
+ const separator = useCallback(() => {
53
+ if (!props?.separators) return null
54
+ return <RenderSeparator separatorStyles={styles.separator} />
55
+ }, [])
37
56
 
38
- const getItemPosition = (section, itemIdx) => {
39
- const listLength = section?.length || 0
57
+ const getSectionProps = (data: SectionRenderComponentProps<T>) => {
58
+ const listLength = sections?.length || 0
40
59
 
41
- const isFirst = itemIdx === 0
42
- const isLast = itemIdx === listLength - 1
60
+ const isFirst = data?.section?.index === sections?.[0]?.index
61
+ const isLast = data?.section?.index === sections?.[listLength - 1]?.index
43
62
  const isOnly = isFirst && isLast
63
+ const title = data?.section?.title
64
+ const index = data?.section?.index
44
65
 
45
- return { isFirst, isLast, isOnly }
66
+ return { isFirst, isLast, isOnly, title, index }
46
67
  }
47
68
 
48
- const getSectionPosition = (data) => {
49
- const listLength = props.sections?.length || 0
69
+ const renderSectionHeader = useCallback((data: SectionRenderComponentProps<T>) => {
70
+ if (!RenderSectionHeader) return null
50
71
 
51
- const isFirst = data.section.key === props.sections[0].key
52
- const isLast = data.section.key === props.sections[listLength - 1].key
53
- const isOnly = isFirst && isLast
72
+ const positionProps = getSectionProps(data)
54
73
 
55
- return { isFirst, isLast, isOnly }
56
- }
74
+ return <RenderSectionHeader {...data.section} {...positionProps} />
75
+ }, [RenderSectionHeader, sections?.length])
57
76
 
58
- const renderSectionHeader = useCallback((data) => {
59
- if (!props?.renderSectionHeader) return null
77
+ const renderSectionFooter = useCallback((data: SectionRenderComponentProps<T>) => {
78
+ if (!RenderSectionFooter) return null
60
79
 
61
- return props?.renderSectionHeader({ ...data, ...getSectionPosition(data) })
62
- }, [props?.renderSectionHeader, props?.sections?.length])
80
+ const positionProps = getSectionProps(data)
63
81
 
64
- const renderSectionFooter = useCallback((data) => {
65
- if (!props?.renderSectionFooter) return null
82
+ return <RenderSectionFooter {...data.section} {...positionProps} />
83
+ }, [RenderSectionFooter, sections?.length])
66
84
 
67
- return props?.renderSectionFooter({ ...data, ...getSectionPosition(data) })
68
- }, [props?.renderSectionFooter, props?.sections?.length])
85
+ const renderItem = useCallback((data: AugmentedSectionRenderItemInfo<T>) => {
86
+ if (!RenderItem) return null
69
87
 
70
- const renderItem = useCallback((data) => {
71
- if (!props?.renderItem) return null
88
+ const listLength = data?.section?.data?.length || 0
72
89
 
73
- return props?.renderItem({ ...data, ...getItemPosition(data.section?.data, data?.index) })
90
+ const isFirst = data?.index === 0
91
+ const isLast = data?.index === listLength - 1
92
+ const isOnly = isFirst && isLast
74
93
 
75
- }, [props?.renderItem, props?.sections?.length])
94
+ return (
95
+ <RenderItem
96
+ {...data}
97
+ isFirst={isFirst}
98
+ isLast={isLast}
99
+ isOnly={isOnly}
100
+ />
101
+ )
102
+ }, [RenderItem])
103
+
104
+ const isEmpty = !sections || !sections?.length
105
+
106
+ const _placeholder = {
107
+ ...placeholder,
108
+ loading: TypeGuards.isBoolean(placeholder?.loading) ? placeholder.loading : loading,
109
+ }
76
110
 
77
- const separatorProp = props.separators
78
- const isEmpty = !props.sections || !props.sections.length
79
- const separator = !isEmpty && separatorProp == true && renderSeparator
111
+ const keyboardStyle = useKeyboardPaddingStyle([
112
+ styles.content,
113
+ contentContainerStyle,
114
+ isEmpty && styles['content:empty'],
115
+ loading && styles['content:loading'],
116
+ ], keyboardAware && !props.horizontal)
80
117
 
81
- const keyboardStyle = useKeyboardPaddingStyle([styles?.content, contentContainerStyle], keyboardAware)
118
+ const wrapperStyle = [styles.wrapper, isEmpty && styles['wrapper:empty'], loading && styles['wrapper:loading']]
82
119
 
83
120
  return (
84
121
  <SectionList
85
- contentContainerStyle={keyboardStyle}
86
- showsVerticalScrollIndicator={false}
87
- // @ts-ignore
88
- ref={ref}
89
122
  ItemSeparatorComponent={separator}
123
+ refreshControl={!!onRefresh && (
124
+ <RefreshControl
125
+ refreshing={refreshing}
126
+ onRefresh={onRefresh}
127
+ {...refreshControlProps}
128
+ />
129
+ )}
130
+ ListEmptyComponent={<EmptyPlaceholder {..._placeholder} />}
131
+ showsVerticalScrollIndicator={false}
132
+ showsHorizontalScrollIndicator={false}
90
133
  {...props}
91
- style={styles?.wrapper}
92
- refreshControl={
93
- !!onRefresh && (
94
- <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
95
- )
96
- }
134
+ ListHeaderComponentStyle={styles.header}
135
+ style={wrapperStyle}
136
+ contentContainerStyle={keyboardStyle}
137
+ sections={sections}
97
138
  renderItem={renderItem}
98
- renderSectionHeader={renderSectionHeader}
99
- renderSectionFooter={renderSectionFooter}
139
+ renderSectionHeader={renderSectionHeader as unknown as RNSectionProps<T>['renderSectionHeader']}
140
+ renderSectionFooter={renderSectionFooter as unknown as RNSectionProps<T>['renderSectionHeader']}
100
141
  />
101
142
  )
102
- },
103
- ) as StyledComponentWithProps<SectionListProps>
143
+ }
104
144
 
105
145
  Sections.styleRegistryName = 'Sections'
106
- Sections.elements = ['wrapper', 'content', 'separator']
146
+ Sections.elements = ['wrapper', 'content', 'separator', 'header', 'refreshControl']
107
147
  Sections.rootElement = 'wrapper'
108
148
 
109
149
  Sections.withVariantTypes = <S extends AnyRecord>(styles: S) => {
110
- return Sections as (props: StyledComponentProps<SectionListProps, typeof styles>) => IJSX
150
+ return Sections as <T>(props: StyledComponentProps<SectionProps<T>, typeof styles>) => IJSX
111
151
  }
112
152
 
113
153
  Sections.defaultProps = {
114
154
  keyboardShouldPersistTaps: 'handled',
155
+ fakeEmpty: false,
156
+ loading: false,
115
157
  keyboardAware: true,
116
- } as Partial<SectionListProps>
158
+ } as Partial<SectionProps>
117
159
 
118
160
  MobileStyleRegistry.registerComponent(Sections)
@@ -1,2 +1,7 @@
1
+ import { ScrollComposition } from '../Scroll/styles'
1
2
 
2
- export type SectionsComposition = 'wrapper' |'content' | 'separator'
3
+ type SectionsStates = 'empty' | 'loading'
4
+
5
+ type SectionsParts = ScrollComposition | 'separator' | 'header' | 'refreshControl'
6
+
7
+ export type SectionsComposition = `${SectionsParts}:${SectionsStates}` | SectionsParts
@@ -1,39 +1,56 @@
1
1
  import { StyledProp } from '@codeleap/styles'
2
- import { SectionListRenderItemInfo } from 'react-native'
3
2
  import { SectionsComposition } from './styles'
4
- import { SectionListProps as RNSectionListProps } from 'react-native'
3
+ import { SectionListRenderItemInfo, SectionListProps as RNSectionListProps, SectionListData } from 'react-native'
5
4
  import { ViewProps } from '../View'
6
5
  import { EmptyPlaceholderProps } from '../EmptyPlaceholder'
7
6
  import { RefreshControlProps } from '../RefreshControl'
8
7
 
9
- export type DataboundSectionListPropsTypes = 'data' | 'renderItem' | 'keyExtractor' | 'getItemLayout' | 'style'
8
+ export type DataboundSectionListPropsTypes = 'sections' | 'renderItem' | 'keyExtractor' | 'style' | 'renderSectionFooter' | 'renderSectionHeader'
10
9
 
11
- export type AugmentedSectionRenderItemInfo<T> = SectionListRenderItemInfo<T> & {
10
+ export type SectionInfo = {
12
11
  isFirst: boolean
13
12
  isLast: boolean
14
13
  isOnly: boolean
15
14
  }
16
15
 
17
- export type ReplaceSectionListProps<P, T> = Omit<P, DataboundSectionListPropsTypes> & {
18
- sections: T[]
19
- keyExtractor?: (item: T, index: number) => string
20
- renderItem: (data: AugmentedSectionRenderItemInfo<T>) => React.ReactElement
21
- onRefresh?: () => void
22
- getItemLayout?: ((data: T, index: number) => { length: number; offset: number; index: number })
23
- fakeEmpty?: boolean
16
+ export type AugmentedSectionRenderItemInfo<T> = SectionListRenderItemInfo<T> & SectionInfo
17
+
18
+ export type SectionComponentProps<T> = SectionInfo & {
19
+ title: string
20
+ index: number
21
+ data: T[]
22
+ }
23
+
24
+ export type SectionRenderComponentProps<T> = {
25
+ section: {
26
+ title: string
27
+ index: number
28
+ data: T[]
29
+ }
24
30
  }
25
31
 
26
- export type SectionListProps<
27
- T = any[],
28
- Data = T extends Array<infer D> ? D : never
29
- > =
30
- ReplaceSectionListProps<RNSectionListProps<Data>, Data> &
32
+ export type ReplaceSectionListProps<P, T> =
33
+ Omit<P, DataboundSectionListPropsTypes> &
34
+ {
35
+ sections: Array<{ title: string; data: T[] }>
36
+ keyExtractor?: (item: T, index: number) => string
37
+ renderItem: (props: AugmentedSectionRenderItemInfo<T>) => React.ReactElement
38
+ onRefresh?: () => void
39
+ fakeEmpty?: boolean
40
+ loading?: boolean
41
+ renderSectionHeader?: (props: SectionComponentProps<T>) => React.ReactElement
42
+ renderSectionFooter?: (props: SectionComponentProps<T>) => React.ReactElement
43
+ }
44
+
45
+ export type SectionProps<T = any> =
46
+ ReplaceSectionListProps<RNSectionListProps<T>, T> &
31
47
  Omit<ViewProps, 'style'> &
32
48
  {
33
49
  separators?: boolean
34
50
  placeholder?: EmptyPlaceholderProps
35
51
  refreshControlProps?: Partial<RefreshControlProps>
36
52
  fakeEmpty?: boolean
53
+ loading?: boolean
37
54
  keyboardAware?: boolean
38
55
  style?: StyledProp<SectionsComposition>
39
56
  }
@@ -40,3 +40,4 @@ export * from './SearchInput'
40
40
  export * from './PaginationIndicator'
41
41
  export * from './PlacesAutocomplete'
42
42
  export * from './SortablePhotos'
43
+ export * from './SectionFilters'