added custom styles for list

This commit is contained in:
PhilReact 2025-03-12 08:47:50 +02:00
parent d5f796281b
commit e4b781c41f
3 changed files with 114 additions and 40 deletions

View File

@ -1,5 +1,6 @@
import { Box, Typography } from "@mui/material" import { Box, Typography } from "@mui/material";
import { BarSpinner } from "./Spinners/BarSpinner/BarSpinner" import { BarSpinner } from "./Spinners/BarSpinner/BarSpinner";
import { CSSProperties } from "react";
interface ListLoaderProps { interface ListLoaderProps {
isLoading: boolean; isLoading: boolean;
@ -7,37 +8,55 @@ interface ListLoaderProps {
resultsLength: number; resultsLength: number;
noResultsMessage?: string; // Optional message when no results noResultsMessage?: string; // Optional message when no results
children: React.ReactNode; // Required, to render the list content children: React.ReactNode; // Required, to render the list content
loaderList?: (status: "LOADING" | "NO_RESULTS") => React.ReactNode; // Function type
loaderHeight?: CSSProperties
} }
export const ListLoader = ({
export const ListLoader = ({ isLoading, loadingMessage, resultsLength, children, noResultsMessage }: ListLoaderProps) => { isLoading,
loadingMessage,
resultsLength,
children,
noResultsMessage,
loaderList,
loaderHeight
}: ListLoaderProps) => {
return ( return (
<> <>
{isLoading && ( {loaderList && isLoading && (
<Box sx={{ <>
display: 'flex', {loaderList("LOADING")}
gap: '20px', </>
alignItems: 'center', )}
overflow: "auto", {loaderList && !isLoading && resultsLength === 0 && (
}}> <>
<BarSpinner width="22px" color="green" /> {loaderList("NO_RESULTS")}
<Typography>{loadingMessage || `Fetching list`}</Typography> </>
</Box> )}
)} {!loaderList && isLoading && (
{!isLoading && resultsLength === 0 && ( <Box
<Typography sx={{
style={{ display: "flex",
display: "block", gap: "20px",
}} alignItems: "center",
> overflow: "auto",
{noResultsMessage} height: loaderHeight || "auto"
</Typography> }}
)} >
{!isLoading && resultsLength > 0 && ( <BarSpinner width="22px" color="green" />
<> <Typography>{loadingMessage || `Fetching list`}</Typography>
{children} </Box>
</> )}
)} {!loaderList && !isLoading && resultsLength === 0 && (
<Typography
style={{
display: "block",
}}
>
{noResultsMessage}
</Typography>
)}
{!isLoading && resultsLength > 0 && <>{children}</>}
</> </>
) );
} };

15
src/common/Spacer.tsx Normal file
View File

@ -0,0 +1,15 @@
import { Box } from "@mui/material";
export const Spacer = ({ height, width, ...props }: any) => {
return (
<Box
sx={{
height: height ? height : '0px',
display: 'flex',
flexShrink: 0,
width: width ? width : '0px',
...(props || {})
}}
/>
);
};

View File

@ -15,16 +15,43 @@ import { ListLoader } from "../../common/ListLoader";
import { ListItem, useCacheStore } from "../../state/cache"; import { ListItem, useCacheStore } from "../../state/cache";
import { ResourceLoader } from "./ResourceLoader"; import { ResourceLoader } from "./ResourceLoader";
import { ItemCardWrapper } from "./ItemCardWrapper"; import { ItemCardWrapper } from "./ItemCardWrapper";
import { Spacer } from "../../common/Spacer";
interface ResourceListStyles {
gap?: number
listLoadingHeight?: CSSProperties
}
interface DefaultLoaderParams {
listLoadingText?: string
listNoResultsText?: string
listItemLoadingText?: string
listItemErrorText?: string
}
interface PropsResourceListDisplay { interface PropsResourceListDisplay {
params: QortalSearchParams; params: QortalSearchParams;
listItem: (item: ListItem, index: number) => React.ReactNode; // Function type listItem: (item: ListItem, index: number) => React.ReactNode; // Function type
styles?: ResourceListStyles
loaderItem?: (status: "LOADING" | "ERROR") => React.ReactNode; // Function type
defaultLoaderParams?: DefaultLoaderParams
loaderList?: (status: "LOADING" | "NO_RESULTS") => React.ReactNode; // Function type
} }
export const ResourceListDisplay = ({ export const ResourceListDisplay = ({
params, params,
listItem, listItem,
styles = {
gap: 1
},
defaultLoaderParams,
loaderItem,
loaderList
}: PropsResourceListDisplay) => { }: PropsResourceListDisplay) => {
const [list, setList] = useState<QortalMetadata[]>([]); const [list, setList] = useState<QortalMetadata[]>([]);
const { fetchResources } = useResources(); const { fetchResources } = useResources();
@ -50,10 +77,12 @@ export const ResourceListDisplay = ({
return ( return (
<ListLoader <ListLoader
noResultsMessage="No results available" noResultsMessage={defaultLoaderParams?.listNoResultsText || "No results available"}
resultsLength={list?.length} resultsLength={list?.length}
isLoading={isLoading} isLoading={isLoading}
loadingMessage="Retrieving list. Please wait." loadingMessage={defaultLoaderParams?.listLoadingText || "Retrieving list. Please wait."}
loaderList={loaderList}
loaderHeight={styles?.listLoadingHeight}
> >
<div <div
style={{ style={{
@ -62,10 +91,15 @@ export const ResourceListDisplay = ({
width: "100%", width: "100%",
}} }}
> >
<div style={{ display: "flex", flexGrow: isLoading ? 0 : 1 }}> <div style={{ display: "flex", flexGrow: 1 }}>
<VirtualizedList list={list}> <VirtualizedList list={list}>
{(item: QortalMetadata, index: number) => ( {(item: QortalMetadata, index: number) => (
<ListItemWrapper item={item} index={index} render={listItem} /> <>
{styles?.gap && <Spacer height={`${styles.gap / 2}rem`} /> }
<Spacer/>
<ListItemWrapper defaultLoaderParams={defaultLoaderParams} item={item} index={index} render={listItem} renderListItemLoader={loaderItem} />
{styles?.gap && <Spacer height={`${styles.gap / 2}rem`} /> }
</>
)} )}
</VirtualizedList> </VirtualizedList>
</div> </div>
@ -78,38 +112,44 @@ interface ListItemWrapperProps {
item: QortalMetadata; item: QortalMetadata;
index: number; index: number;
render: (item: ListItem, index: number) => React.ReactNode; render: (item: ListItem, index: number) => React.ReactNode;
defaultLoaderParams?: DefaultLoaderParams;
renderListItemLoader?:(status: "LOADING" | "ERROR")=> React.ReactNode;
} }
const ListItemWrapper: React.FC<ListItemWrapperProps> = ({ const ListItemWrapper: React.FC<ListItemWrapperProps> = ({
item, item,
index, index,
render, render,
defaultLoaderParams,
renderListItemLoader
}) => { }) => {
console.log("Rendering wrapped item:", item, index);
const getResourceCache = useCacheStore().getResourceCache; const getResourceCache = useCacheStore().getResourceCache;
const findCachedResource = getResourceCache( const findCachedResource = getResourceCache(
`${item.service}-${item.name}-${item.identifier}`, `${item.service}-${item.name}-${item.identifier}`,
true true
); );
if (findCachedResource === null) if (findCachedResource === null && !renderListItemLoader)
return ( return (
<ItemCardWrapper height={60} isInCart={false}> <ItemCardWrapper height={60} isInCart={false}>
<ResourceLoader <ResourceLoader
message="Fetching Data..." message={defaultLoaderParams?.listItemLoadingText || "Fetching Data..."}
status="loading" status="loading"
/> />
</ItemCardWrapper> </ItemCardWrapper>
); );
if (findCachedResource === false) if (findCachedResource === false && !renderListItemLoader)
return ( return (
<ItemCardWrapper height={60} isInCart={false}> <ItemCardWrapper height={60} isInCart={false}>
<ResourceLoader <ResourceLoader
message="Product is unavailble at this moment... Try again later." message={defaultLoaderParams?.listItemErrorText ||"Resource is unavailble at this moment... Try again later."}
status="error" status="error"
/> />
</ItemCardWrapper> </ItemCardWrapper>
); );
if(renderListItemLoader && (findCachedResource === false || findCachedResource === null)){
return renderListItemLoader(findCachedResource === null ? "LOADING" : "ERROR")
}
// Example transformation (Modify item if needed) // Example transformation (Modify item if needed)
const transformedItem = findCachedResource const transformedItem = findCachedResource