-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
ItemListSkeletonView.tsx
96 lines (83 loc) · 3.21 KB
/
ItemListSkeletonView.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import React, {useCallback, useMemo, useState} from 'react';
import type {LayoutChangeEvent, StyleProp, ViewStyle} from 'react-native';
import {StyleSheet, View} from 'react-native';
import SkeletonViewContentLoader from '@components/SkeletonViewContentLoader';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
type ListItemSkeletonProps = {
shouldAnimate?: boolean;
renderSkeletonItem: (args: {itemIndex: number}) => React.ReactNode;
fixedNumItems?: number;
gradientOpacityEnabled?: boolean;
itemViewStyle?: StyleProp<ViewStyle>;
itemViewHeight?: number;
};
const getVerticalMargin = (style: StyleProp<ViewStyle>): number => {
if (!style) {
return 0;
}
const flattenStyle = StyleSheet.flatten(style);
const marginVertical = Number(flattenStyle?.marginVertical ?? 0);
const marginTop = Number(flattenStyle?.marginTop ?? 0);
const marginBottom = Number(flattenStyle?.marginBottom ?? 0);
return marginVertical + marginTop + marginBottom;
};
function ItemListSkeletonView({
shouldAnimate = true,
renderSkeletonItem,
fixedNumItems,
gradientOpacityEnabled = false,
itemViewStyle = {},
itemViewHeight = CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT,
}: ListItemSkeletonProps) {
const theme = useTheme();
const themeStyles = useThemeStyles();
const [numItems, setNumItems] = useState(fixedNumItems ?? 0);
const totalItemHeight = itemViewHeight + getVerticalMargin(itemViewStyle);
const handleLayout = useCallback(
(event: LayoutChangeEvent) => {
if (fixedNumItems) {
return;
}
const totalHeight = event.nativeEvent.layout.height;
const newNumItems = Math.ceil(totalHeight / totalItemHeight);
if (newNumItems !== numItems) {
setNumItems(newNumItems);
}
},
[fixedNumItems, numItems, totalItemHeight],
);
const skeletonViewItems = useMemo(() => {
const items = [];
for (let i = 0; i < numItems; i++) {
const opacity = gradientOpacityEnabled ? 1 - i / (numItems - 1) : 1;
items.push(
<View
key={`skeletonContainer${i}`}
style={[themeStyles.mr5, itemViewStyle, {opacity}]}
>
<SkeletonViewContentLoader
animate={shouldAnimate}
height={itemViewHeight}
backgroundColor={theme.skeletonLHNIn}
foregroundColor={theme.skeletonLHNOut}
>
{renderSkeletonItem({itemIndex: i})}
</SkeletonViewContentLoader>
</View>,
);
}
return items;
}, [numItems, shouldAnimate, theme, themeStyles, renderSkeletonItem, gradientOpacityEnabled, itemViewHeight, itemViewStyle]);
return (
<View
style={[themeStyles.flex1, themeStyles.overflowHidden]}
onLayout={handleLayout}
>
{skeletonViewItems}
</View>
);
}
ItemListSkeletonView.displayName = 'ListItemSkeleton';
export default ItemListSkeletonView;