Skip to content

Commit

Permalink
Merge pull request #40682 from software-mansion-labs/kicu/search-v1/s…
Browse files Browse the repository at this point in the history
…earch-skeleton

[Search v1] Add empty search List skeleton
  • Loading branch information
luacmartins authored Apr 23, 2024
2 parents 3a3cef6 + d4670d4 commit f30d4a0
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 68 deletions.
110 changes: 42 additions & 68 deletions src/components/OptionsListSkeletonView.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,54 @@
import React, {useMemo, useState} from 'react';
import {View} from 'react-native';
import React from 'react';
import {Circle, Rect} from 'react-native-svg';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import SkeletonViewContentLoader from './SkeletonViewContentLoader';
import ItemListSkeletonView from './Skeletons/ItemListSkeletonView';

function getLinedWidth(index: number): string {
const step = index % 3;
if (step === 0) {
return '100%';
}

if (step === 1) {
return '50%';
}

return '25%';
}

type OptionsListSkeletonViewProps = {
shouldAnimate?: boolean;
};

function OptionsListSkeletonView({shouldAnimate = true}: OptionsListSkeletonViewProps) {
const theme = useTheme();
const themeStyles = useThemeStyles();

const [numItems, setNumItems] = useState(0);
const skeletonViewItems = useMemo(() => {
const items = [];
for (let i = 0; i < numItems; i++) {
const step = i % 3;
let lineWidth;
switch (step) {
case 0:
lineWidth = '100%';
break;
case 1:
lineWidth = '50%';
break;
default:
lineWidth = '25%';
}
items.push(
<SkeletonViewContentLoader
key={`skeletonViewItems${i}`}
animate={shouldAnimate}
height={CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT}
backgroundColor={theme.skeletonLHNIn}
foregroundColor={theme.skeletonLHNOut}
style={themeStyles.mr5}
>
<Circle
cx="40"
cy="32"
r="20"
/>
<Rect
x="72"
y="18"
width="20%"
height="8"
/>
<Rect
x="72"
y="38"
width={lineWidth}
height="8"
/>
</SkeletonViewContentLoader>,
);
}
return items;
}, [numItems, shouldAnimate, theme, themeStyles]);

return (
<View
style={[themeStyles.flex1, themeStyles.overflowHidden]}
onLayout={(event) => {
const newNumItems = Math.ceil(event.nativeEvent.layout.height / CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT);
if (newNumItems === numItems) {
return;
}
setNumItems(newNumItems);
<ItemListSkeletonView
shouldAnimate={shouldAnimate}
renderSkeletonItem={({itemIndex}) => {
const lineWidth = getLinedWidth(itemIndex);

return (
<>
<Circle
cx="40"
cy="32"
r="20"
/>
<Rect
x="72"
y="18"
width="20%"
height="8"
/>
<Rect
x="72"
y="38"
width={lineWidth}
height="8"
/>
</>
);
}}
>
<View>{skeletonViewItems}</View>
</View>
/>
);
}

Expand Down
55 changes: 55 additions & 0 deletions src/components/Skeletons/ItemListSkeletonView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, {useMemo, useState} from 'react';
import {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;
};

function ItemListSkeletonView({shouldAnimate = true, renderSkeletonItem}: ListItemSkeletonProps) {
const theme = useTheme();
const themeStyles = useThemeStyles();

const [numItems, setNumItems] = useState(0);
const skeletonViewItems = useMemo(() => {
const items = [];
for (let i = 0; i < numItems; i++) {
items.push(
<SkeletonViewContentLoader
key={`skeletonViewItems${i}`}
animate={shouldAnimate}
height={CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT}
backgroundColor={theme.skeletonLHNIn}
foregroundColor={theme.skeletonLHNOut}
style={themeStyles.mr5}
>
{renderSkeletonItem({itemIndex: i})}
</SkeletonViewContentLoader>,
);
}
return items;
}, [numItems, shouldAnimate, theme, themeStyles, renderSkeletonItem]);

return (
<View
style={[themeStyles.flex1, themeStyles.overflowHidden]}
onLayout={(event) => {
const newNumItems = Math.ceil(event.nativeEvent.layout.height / CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT);
if (newNumItems === numItems) {
return;
}
setNumItems(newNumItems);
}}
>
<View>{skeletonViewItems}</View>
</View>
);
}

ItemListSkeletonView.displayName = 'ListItemSkeleton';

export default ItemListSkeletonView;
65 changes: 65 additions & 0 deletions src/components/Skeletons/TableListItemSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';
import {Rect} from 'react-native-svg';
import ItemListSkeletonView from './ItemListSkeletonView';

type TableListItemSkeletonProps = {
shouldAnimate?: boolean;
};

const barHeight = '10';
const shortBarWidth = '40';
const longBarWidth = '120';

function TableListItemSkeleton({shouldAnimate = true}: TableListItemSkeletonProps) {
return (
<ItemListSkeletonView
shouldAnimate={shouldAnimate}
renderSkeletonItem={() => (
<>
<Rect
x="20"
y="10"
rx="5"
ry="5"
width="40"
height="40"
/>
<Rect
x="80"
y="25"
width={shortBarWidth}
height={barHeight}
/>
<Rect
x="150"
y="25"
width={longBarWidth}
height={barHeight}
/>
<Rect
x="320"
y="25"
width={longBarWidth}
height={barHeight}
/>
<Rect
x="480"
y="25"
width={longBarWidth}
height={barHeight}
/>
<Rect
x="660"
y="25"
width="100%"
height={barHeight}
/>
</>
)}
/>
);
}

TableListItemSkeleton.displayName = 'TableListItemSkeleton';

export default TableListItemSkeleton;
10 changes: 10 additions & 0 deletions src/pages/Search/EmptySearchView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import TableListItemSkeleton from '@components/Skeletons/TableListItemSkeleton';

function EmptySearchView() {
return <TableListItemSkeleton shouldAnimate />;
}

EmptySearchView.displayName = 'EmptySearchView';

export default EmptySearchView;
2 changes: 2 additions & 0 deletions src/pages/Search/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import ScreenWrapper from '@components/ScreenWrapper';
import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types';
import type SCREENS from '@src/SCREENS';
// import EmptySearchView from './EmptySearchView';
import SearchResults from './SearchResults';
import useCustomBackHandler from './useCustomBackHandler';

Expand All @@ -14,6 +15,7 @@ function SearchPage({route}: SearchPageProps) {
return (
<ScreenWrapper testID={SearchPage.displayName}>
<SearchResults query={route.params.query} />
{/* <EmptySearchView /> */}
</ScreenWrapper>
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/pages/Search/SearchPageBottomTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type IconAsset from '@src/types/utils/IconAsset';

// import EmptySearchView from './EmptySearchView';

type SearchMenuItem = {
title: string;
icon: IconAsset;
Expand Down Expand Up @@ -59,6 +61,7 @@ function SearchPageBottomTab() {
/>
))}
</View>
{/* <EmptySearchView /> */}
</ScreenWrapper>
);
}
Expand Down

0 comments on commit f30d4a0

Please sign in to comment.