@codeleap/web 3.12.5 → 3.12.7
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.
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import { EmptyPlaceholder } from '../EmptyPlaceholder'
|
|
|
5
5
|
import { GridPresets } from './styles'
|
|
6
6
|
import { GridProps } from './types'
|
|
7
7
|
import { ListLayout, useInfiniteScroll } from '../List'
|
|
8
|
-
import { ItemMasonryProps, ListMasonry } from '../../lib'
|
|
8
|
+
import { ItemMasonryProps, ListMasonry, useMasonryReload } from '../../lib'
|
|
9
9
|
|
|
10
10
|
export * from './styles'
|
|
11
11
|
export * from './types'
|
|
@@ -30,7 +30,8 @@ const defaultProps: Partial<GridProps> = {
|
|
|
30
30
|
columnItemsSpacing: 8,
|
|
31
31
|
rowItemsSpacing: 8,
|
|
32
32
|
overscan: 2,
|
|
33
|
-
|
|
33
|
+
reloadTimeout: 350,
|
|
34
|
+
showFooter: true,
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export function Grid<T = any>(props: GridProps<T>) {
|
|
@@ -52,7 +53,8 @@ export function Grid<T = any>(props: GridProps<T>) {
|
|
|
52
53
|
separators,
|
|
53
54
|
masonryProps = {},
|
|
54
55
|
numColumns,
|
|
55
|
-
|
|
56
|
+
reloadTimeout,
|
|
57
|
+
showFooter,
|
|
56
58
|
} = allProps
|
|
57
59
|
|
|
58
60
|
const variantStyles = useDefaultComponentStyle<'u:Grid', typeof GridPresets>('u:Grid', {
|
|
@@ -61,7 +63,12 @@ export function Grid<T = any>(props: GridProps<T>) {
|
|
|
61
63
|
styles,
|
|
62
64
|
})
|
|
63
65
|
|
|
64
|
-
const { layoutProps, onLoadMore
|
|
66
|
+
const { layoutProps, onLoadMore } = useInfiniteScroll(allProps)
|
|
67
|
+
|
|
68
|
+
const { reloadingLayout, previousLength } = useMasonryReload({
|
|
69
|
+
data,
|
|
70
|
+
reloadTimeout,
|
|
71
|
+
})
|
|
65
72
|
|
|
66
73
|
const separator = React.useMemo(() => {
|
|
67
74
|
return separators ? <ListSeparatorComponent separatorStyles={variantStyles.separator} /> : null
|
|
@@ -97,19 +104,20 @@ export function Grid<T = any>(props: GridProps<T>) {
|
|
|
97
104
|
{...allProps}
|
|
98
105
|
{...layoutProps}
|
|
99
106
|
variantStyles={variantStyles}
|
|
107
|
+
showFooter={reloadingLayout ? false : showFooter}
|
|
100
108
|
>
|
|
101
109
|
<ListMasonry
|
|
102
110
|
items={data}
|
|
103
111
|
render={renderItem}
|
|
104
|
-
itemKey={item => item?.id}
|
|
112
|
+
itemKey={(item, _index) => (item?.id ?? _index)}
|
|
105
113
|
columnGutter={columnItemsSpacing}
|
|
106
114
|
rowGutter={rowItemsSpacing}
|
|
107
115
|
columnCount={numColumns}
|
|
108
116
|
maxColumnCount={numColumns}
|
|
109
117
|
onRender={onLoadMore}
|
|
110
118
|
overscanBy={overscan}
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
previousItemsLength={previousLength}
|
|
120
|
+
reloadingLayout={reloadingLayout}
|
|
113
121
|
{...masonryProps}
|
|
114
122
|
/>
|
|
115
123
|
</ListLayout>
|
|
@@ -10,6 +10,7 @@ import { motion } from 'framer-motion'
|
|
|
10
10
|
type ListLayoutProps = Omit<ListProps, 'renderItem'> & UseInfiniteScrollReturn['layoutProps'] & {
|
|
11
11
|
variantStyles: StylesOf<ListComposition>
|
|
12
12
|
children?: React.ReactNode
|
|
13
|
+
showFooter?: boolean
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
type ListRefreshControlComponent = Partial<ListLayoutProps> & {
|
|
@@ -65,6 +66,7 @@ export const ListLayout = (props: ListLayoutProps) => {
|
|
|
65
66
|
isFetchingNextPage,
|
|
66
67
|
ListLoadingIndicatorComponent,
|
|
67
68
|
scrollableRef,
|
|
69
|
+
showFooter = true,
|
|
68
70
|
} = props
|
|
69
71
|
|
|
70
72
|
const getKeyStyle = React.useCallback((key: ListParts) => ([
|
|
@@ -94,7 +96,7 @@ export const ListLayout = (props: ListLayoutProps) => {
|
|
|
94
96
|
? <ListLoadingIndicatorComponent />
|
|
95
97
|
: null}
|
|
96
98
|
|
|
97
|
-
{!!ListFooterComponent ? <ListFooterComponent /> : null}
|
|
99
|
+
{(!!ListFooterComponent && showFooter) ? <ListFooterComponent /> : null}
|
|
98
100
|
</View>
|
|
99
101
|
)
|
|
100
102
|
}
|
|
@@ -6,7 +6,7 @@ import { ListPresets } from './styles'
|
|
|
6
6
|
import { useInfiniteScroll } from './useInfiniteScroll'
|
|
7
7
|
import { ListProps } from './types'
|
|
8
8
|
import { ListLayout } from './ListLayout'
|
|
9
|
-
import { ItemMasonryProps, ListMasonry } from '../../lib'
|
|
9
|
+
import { ItemMasonryProps, ListMasonry, useMasonryReload } from '../../lib'
|
|
10
10
|
|
|
11
11
|
export * from './styles'
|
|
12
12
|
export * from './PaginationIndicator'
|
|
@@ -33,7 +33,8 @@ const defaultProps: Partial<ListProps> = {
|
|
|
33
33
|
refresh: true,
|
|
34
34
|
rowItemsSpacing: 8,
|
|
35
35
|
overscan: 2,
|
|
36
|
-
|
|
36
|
+
reloadTimeout: 350,
|
|
37
|
+
showFooter: true,
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export function List<T = any>(props: ListProps<T>) {
|
|
@@ -53,7 +54,8 @@ export function List<T = any>(props: ListProps<T>) {
|
|
|
53
54
|
overscan,
|
|
54
55
|
separators,
|
|
55
56
|
masonryProps = {},
|
|
56
|
-
|
|
57
|
+
reloadTimeout,
|
|
58
|
+
showFooter,
|
|
57
59
|
} = allProps
|
|
58
60
|
|
|
59
61
|
const variantStyles = useDefaultComponentStyle<'u:List', typeof ListPresets>('u:List', {
|
|
@@ -62,7 +64,12 @@ export function List<T = any>(props: ListProps<T>) {
|
|
|
62
64
|
styles,
|
|
63
65
|
})
|
|
64
66
|
|
|
65
|
-
const { layoutProps, onLoadMore
|
|
67
|
+
const { layoutProps, onLoadMore } = useInfiniteScroll(allProps)
|
|
68
|
+
|
|
69
|
+
const { reloadingLayout, previousLength } = useMasonryReload({
|
|
70
|
+
data,
|
|
71
|
+
reloadTimeout,
|
|
72
|
+
})
|
|
66
73
|
|
|
67
74
|
const separator = React.useMemo(() => {
|
|
68
75
|
return separators ? <ListSeparatorComponent separatorStyles={variantStyles.separator} /> : null
|
|
@@ -96,17 +103,18 @@ export function List<T = any>(props: ListProps<T>) {
|
|
|
96
103
|
{...allProps}
|
|
97
104
|
{...layoutProps}
|
|
98
105
|
variantStyles={variantStyles}
|
|
106
|
+
showFooter={reloadingLayout ? false : showFooter}
|
|
99
107
|
>
|
|
100
108
|
<ListMasonry
|
|
101
109
|
items={data}
|
|
102
110
|
render={renderItem}
|
|
103
|
-
itemKey={item => item?.id}
|
|
111
|
+
itemKey={(item, _index) => (item?.id ?? _index)}
|
|
104
112
|
rowGutter={rowItemsSpacing}
|
|
105
113
|
onRender={onLoadMore}
|
|
106
114
|
overscanBy={overscan}
|
|
107
115
|
columnCount={1}
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
previousItemsLength={previousLength}
|
|
117
|
+
reloadingLayout={reloadingLayout}
|
|
110
118
|
{...masonryProps}
|
|
111
119
|
/>
|
|
112
120
|
</ListLayout>
|
|
@@ -5,10 +5,10 @@ import { ListComposition, ListPresets } from './styles'
|
|
|
5
5
|
import { motion } from 'framer-motion'
|
|
6
6
|
import { ActivityIndicatorProps } from '../ActivityIndicator'
|
|
7
7
|
import { ComponentCommonProps } from '../../types'
|
|
8
|
-
import { RenderComponentProps, ListProps as ListMasonryProps } from 'masonic'
|
|
9
8
|
import { UseInfiniteScrollArgs } from './useInfiniteScroll'
|
|
9
|
+
import { ItemMasonryProps, ListMasonryProps } from '../../lib'
|
|
10
10
|
|
|
11
|
-
export type AugmentedRenderItemInfo<T> =
|
|
11
|
+
export type AugmentedRenderItemInfo<T> = ItemMasonryProps<T> & {
|
|
12
12
|
item: T
|
|
13
13
|
isFirst: boolean
|
|
14
14
|
isLast: boolean
|
|
@@ -51,5 +51,6 @@ Data = T extends Array<infer D> ? D : never
|
|
|
51
51
|
rowItemsSpacing?: number
|
|
52
52
|
overscan?: number
|
|
53
53
|
masonryProps?: Partial<ListMasonryProps<T>>
|
|
54
|
-
|
|
54
|
+
reloadTimeout?: number
|
|
55
|
+
showFooter?: boolean
|
|
55
56
|
} & ComponentCommonProps & UseInfiniteScrollArgs
|
package/src/lib/ListMasonry.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { useWindowSize } from '@react-hook/window-size'
|
|
3
|
-
import {
|
|
3
|
+
import { TypeGuards } from '@codeleap/common'
|
|
4
|
+
import { EmptyPlaceholder } from '../components/EmptyPlaceholder'
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
useMasonry,
|
|
@@ -12,39 +13,69 @@ import {
|
|
|
12
13
|
RenderComponentProps,
|
|
13
14
|
} from 'masonic'
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
function fillItems(arr: Array<any>, toCount: number, fillContent = {}) {
|
|
17
|
+
if (!arr || !TypeGuards.isArray(arr)) return []
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
if (toCount === arr?.length) return arr
|
|
20
|
+
|
|
21
|
+
const diff = toCount - arr?.length
|
|
22
|
+
|
|
23
|
+
if (diff < 0) return arr
|
|
24
|
+
|
|
25
|
+
const right = Array(diff).fill(fillContent)
|
|
26
|
+
|
|
27
|
+
return arr.concat(right)
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
type UseMasonryReloadArgs = {
|
|
31
|
+
data: Array<any>
|
|
32
|
+
reloadTimeout: number
|
|
33
|
+
}
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
export const useMasonryReload = (args: UseMasonryReloadArgs) => {
|
|
36
|
+
const {
|
|
37
|
+
data,
|
|
38
|
+
reloadTimeout = 350,
|
|
39
|
+
} = args
|
|
26
40
|
|
|
27
|
-
const
|
|
41
|
+
const [reloadingLayout, setReloadingLayout] = React.useState(false)
|
|
42
|
+
const previousLengthRef = React.useRef(data?.length ?? 0)
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
length: data?.length
|
|
33
|
-
})
|
|
44
|
+
const updater = () => {
|
|
45
|
+
previousLengthRef.current = (data?.length ?? 0)
|
|
46
|
+
}
|
|
34
47
|
|
|
35
|
-
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
if (previousLengthRef.current > data?.length) {
|
|
36
50
|
setReloadingLayout(true)
|
|
37
51
|
|
|
38
|
-
console.log({
|
|
39
|
-
reloaded: true,
|
|
40
|
-
})
|
|
41
|
-
|
|
42
52
|
setTimeout(() => {
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
updater()
|
|
54
|
+
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
setReloadingLayout(false)
|
|
57
|
+
}, reloadTimeout)
|
|
58
|
+
}, reloadTimeout)
|
|
59
|
+
} else {
|
|
60
|
+
updater()
|
|
45
61
|
}
|
|
46
|
-
}, [data?.length
|
|
62
|
+
}, [data?.length])
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
reloadingLayout,
|
|
66
|
+
setReloadingLayout,
|
|
67
|
+
previousLength: previousLengthRef.current,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
47
70
|
|
|
71
|
+
export type ItemMasonryProps<T> = RenderComponentProps<T>
|
|
72
|
+
|
|
73
|
+
export type ListMasonryProps<T> = MasonryProps<T> & {
|
|
74
|
+
previousItemsLength: number
|
|
75
|
+
reloadingLayout: boolean
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function MasonryComponent<Item>(props: ListMasonryProps<Item>) {
|
|
48
79
|
const containerRef = React.useRef(null)
|
|
49
80
|
|
|
50
81
|
const windowSize = useWindowSize({
|
|
@@ -72,22 +103,33 @@ export function ListMasonry<Item>(props: ListMasonryProps<Item>) {
|
|
|
72
103
|
columnCount: listProps?.columnCount,
|
|
73
104
|
maxColumnCount: listProps?.columnCount,
|
|
74
105
|
rowGutter: listProps?.rowGutter
|
|
75
|
-
}
|
|
106
|
+
})
|
|
76
107
|
|
|
77
108
|
const { scrollTop, isScrolling } = useScroller(listProps?.offset, listProps?.scrollFps)
|
|
78
109
|
|
|
79
110
|
const resizeObserver = useResizeObserver(positioner)
|
|
80
111
|
|
|
81
|
-
if (reloadingLayout) {
|
|
82
|
-
return <div><p>Loading</p></div>
|
|
83
|
-
}
|
|
84
|
-
|
|
85
112
|
return useMasonry({
|
|
86
113
|
...listProps,
|
|
87
114
|
resizeObserver,
|
|
88
115
|
positioner,
|
|
89
116
|
scrollTop,
|
|
90
117
|
isScrolling,
|
|
91
|
-
items:
|
|
118
|
+
items: fillItems(props?.items, props?.previousItemsLength)
|
|
92
119
|
})
|
|
93
120
|
}
|
|
121
|
+
|
|
122
|
+
export function ListMasonry<Item>(props: ListMasonryProps<Item>) {
|
|
123
|
+
if (props?.reloadingLayout) {
|
|
124
|
+
return (
|
|
125
|
+
<EmptyPlaceholder loading title={''} description={null} />
|
|
126
|
+
)
|
|
127
|
+
} else {
|
|
128
|
+
return (
|
|
129
|
+
<MasonryComponent
|
|
130
|
+
{...props}
|
|
131
|
+
items={props?.items || []}
|
|
132
|
+
/>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
}
|