@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/web",
3
- "version": "3.12.5",
3
+ "version": "3.12.7",
4
4
  "main": "src/index.ts",
5
5
  "repository": {
6
6
  "url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
@@ -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
- enabledItemsRehydrateIndicator: true
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
- enabledItemsRehydrateIndicator,
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, onRefreshItems } = useInfiniteScroll(allProps)
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
- onRefreshItems={onRefreshItems}
112
- itemsRehydrateIndicator={enabledItemsRehydrateIndicator}
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
- enabledItemsRehydrateIndicator: true,
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
- enabledItemsRehydrateIndicator,
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, onRefreshItems } = useInfiniteScroll(allProps)
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
- onRefreshItems={onRefreshItems}
109
- itemsRehydrateIndicator={enabledItemsRehydrateIndicator}
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> = RenderComponentProps<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
- enabledItemsRehydrateIndicator?: boolean
54
+ reloadTimeout?: number
55
+ showFooter?: boolean
55
56
  } & ComponentCommonProps & UseInfiniteScrollArgs
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
2
  import { useWindowSize } from '@react-hook/window-size'
3
- import { AnyFunction, onUpdate, usePrevious, useState } from '@codeleap/common'
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
- export type ItemMasonryProps<T> = RenderComponentProps<T>
16
+ function fillItems(arr: Array<any>, toCount: number, fillContent = {}) {
17
+ if (!arr || !TypeGuards.isArray(arr)) return []
16
18
 
17
- export type ListMasonryProps<T> = MasonryProps<T> & {
18
- onRefreshItems: AnyFunction
19
- itemsRehydrateIndicator: boolean
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
- export function ListMasonry<Item>(props: ListMasonryProps<Item>) {
23
- const data = props?.items || []
30
+ type UseMasonryReloadArgs = {
31
+ data: Array<any>
32
+ reloadTimeout: number
33
+ }
24
34
 
25
- const [reloadingLayout, setReloadingLayout] = useState(false)
35
+ export const useMasonryReload = (args: UseMasonryReloadArgs) => {
36
+ const {
37
+ data,
38
+ reloadTimeout = 350,
39
+ } = args
26
40
 
27
- const dataPreviousLength = usePrevious(data?.length)
41
+ const [reloadingLayout, setReloadingLayout] = React.useState(false)
42
+ const previousLengthRef = React.useRef(data?.length ?? 0)
28
43
 
29
- onUpdate(() => {
30
- console.log({
31
- dataPreviousLength,
32
- length: data?.length
33
- })
44
+ const updater = () => {
45
+ previousLengthRef.current = (data?.length ?? 0)
46
+ }
34
47
 
35
- if (dataPreviousLength > data?.length) {
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
- setReloadingLayout(false)
44
- }, 3000)
53
+ updater()
54
+
55
+ setTimeout(() => {
56
+ setReloadingLayout(false)
57
+ }, reloadTimeout)
58
+ }, reloadTimeout)
59
+ } else {
60
+ updater()
45
61
  }
46
- }, [data?.length, dataPreviousLength])
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
- }, [reloadingLayout])
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: data,
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
+ }