using ArticleCards of various sizes of 6,4,2 instead of hardcoded repeated code

This commit is contained in:
2025-11-12 04:43:21 +05:30
parent 6d951b9ab5
commit e75beaac48
5 changed files with 427 additions and 188 deletions

View File

@@ -0,0 +1,126 @@
import React from 'react';
import { Typography } from '@mui/material';
import Box from "@mui/material/Box";
import AvatarGroup from "@mui/material/AvatarGroup";
import Avatar from "@mui/material/Avatar";
import { styled } from "@mui/material/styles";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import { ArticleCardProps } from "../../types/props";
const StyledCard = styled(Card)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
padding: 0,
height: '100%',
backgroundColor: (theme.vars || theme).palette.background.paper,
'&:hover': {
backgroundColor: 'transparent',
cursor: 'pointer',
},
'&:focus-visible': {
outline: '3px solid',
outlineColor: 'hsla(210, 98%, 48%, 0.5)',
outlineOffset: '2px',
},
}));
const StyledCardContent = styled(CardContent)({
display: 'flex',
flexDirection: 'column',
gap: 4,
padding: 16,
flexGrow: 1,
'&:last-child': {
paddingBottom: 16,
},
});
const StyledTypography = styled(Typography)({
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: 2,
overflow: 'hidden',
textOverflow: 'ellipsis',
});
function Author({ authors }: { authors: { name: string; avatar: string }[] }) {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'row',
gap: 2,
alignItems: 'center',
justifyContent: 'space-between',
padding: '16px',
}}
>
<Box
sx={{ display: 'flex', flexDirection: 'row', gap: 1, alignItems: 'center' }}
>
<AvatarGroup max={3}>
{authors.map((author, index) => (
<Avatar
key={index}
alt={author.name}
src={author.avatar}
sx={{ width: 24, height: 24 }}
/>
))}
</AvatarGroup>
<Typography variant="caption">
{authors.map((author) => author.name).join(', ')}
</Typography>
</Box>
<Typography variant="caption">July 14, 2021</Typography>
</Box>
);
}
export default function ArticleCardSize2({
article,
index,
focusedCardIndex,
onSelectArticle,
onFocus,
onBlur,
}: ArticleCardProps) {
return (
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(index)}
onFocus={() => onFocus(index)}
onBlur={onBlur}
tabIndex={0}
className={focusedCardIndex === index ? 'Mui-focused' : ''}
>
<StyledCardContent
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%',
}}
>
<div>
<Typography gutterBottom variant="caption" component="div">
{article.tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{article.title}
</Typography>
<StyledTypography
variant="body2"
color="text.secondary"
gutterBottom
>
{article.description}
</StyledTypography>
</div>
</StyledCardContent>
<Author authors={article.authors} />
</StyledCard>
);
};

View File

@@ -0,0 +1,122 @@
import React from 'react';
import { CardMedia, Typography } from '@mui/material';
import Box from "@mui/material/Box";
import AvatarGroup from "@mui/material/AvatarGroup";
import Avatar from "@mui/material/Avatar";
import { styled } from "@mui/material/styles";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import { ArticleCardProps } from "../../types/props";
const StyledCard = styled(Card)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
padding: 0,
height: '100%',
backgroundColor: (theme.vars || theme).palette.background.paper,
'&:hover': {
backgroundColor: 'transparent',
cursor: 'pointer',
},
'&:focus-visible': {
outline: '3px solid',
outlineColor: 'hsla(210, 98%, 48%, 0.5)',
outlineOffset: '2px',
},
}));
const StyledCardContent = styled(CardContent)({
display: 'flex',
flexDirection: 'column',
gap: 4,
padding: 16,
flexGrow: 1,
'&:last-child': {
paddingBottom: 16,
},
});
const StyledTypography = styled(Typography)({
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: 2,
overflow: 'hidden',
textOverflow: 'ellipsis',
});
function Author({ authors }: { authors: { name: string; avatar: string }[] }) {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'row',
gap: 2,
alignItems: 'center',
justifyContent: 'space-between',
padding: '16px',
}}
>
<Box
sx={{ display: 'flex', flexDirection: 'row', gap: 1, alignItems: 'center' }}
>
<AvatarGroup max={3}>
{authors.map((author, index) => (
<Avatar
key={index}
alt={author.name}
src={author.avatar}
sx={{ width: 24, height: 24 }}
/>
))}
</AvatarGroup>
<Typography variant="caption">
{authors.map((author) => author.name).join(', ')}
</Typography>
</Box>
<Typography variant="caption">July 14, 2021</Typography>
</Box>
);
}
export default function ArticleCardSize4({
article,
index,
focusedCardIndex,
onSelectArticle,
onFocus,
onBlur,
}: ArticleCardProps) {
return (
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(index)}
onFocus={() => onFocus(index)}
onBlur={onBlur}
tabIndex={0}
className={focusedCardIndex === index ? 'Mui-focused' : ''}
>
<CardMedia
component="img"
alt="green iguana"
image={article.img}
sx={{
height: { sm: 'auto', md: '50%' },
aspectRatio: { sm: '16 / 9', md: '' },
}}
/>
<StyledCardContent>
<Typography gutterBottom variant="caption" component="div">
{article.tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{article.title}
</Typography>
<StyledTypography variant="body2" color="text.secondary" gutterBottom>
{article.description}
</StyledTypography>
</StyledCardContent>
<Author authors={article.authors} />
</StyledCard>
);
};

View File

@@ -0,0 +1,123 @@
import React from 'react';
import { CardMedia, Typography } from '@mui/material';
import Box from "@mui/material/Box";
import AvatarGroup from "@mui/material/AvatarGroup";
import Avatar from "@mui/material/Avatar";
import { styled } from "@mui/material/styles";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import { ArticleCardProps } from "../../types/props";
const StyledCard = styled(Card)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
padding: 0,
height: '100%',
backgroundColor: (theme.vars || theme).palette.background.paper,
'&:hover': {
backgroundColor: 'transparent',
cursor: 'pointer',
},
'&:focus-visible': {
outline: '3px solid',
outlineColor: 'hsla(210, 98%, 48%, 0.5)',
outlineOffset: '2px',
},
}));
const StyledCardContent = styled(CardContent)({
display: 'flex',
flexDirection: 'column',
gap: 4,
padding: 16,
flexGrow: 1,
'&:last-child': {
paddingBottom: 16,
},
});
const StyledTypography = styled(Typography)({
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: 2,
overflow: 'hidden',
textOverflow: 'ellipsis',
});
function Author({ authors }: { authors: { name: string; avatar: string }[] }) {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'row',
gap: 2,
alignItems: 'center',
justifyContent: 'space-between',
padding: '16px',
}}
>
<Box
sx={{ display: 'flex', flexDirection: 'row', gap: 1, alignItems: 'center' }}
>
<AvatarGroup max={3}>
{authors.map((author, index) => (
<Avatar
key={index}
alt={author.name}
src={author.avatar}
sx={{ width: 24, height: 24 }}
/>
))}
</AvatarGroup>
<Typography variant="caption">
{authors.map((author) => author.name).join(', ')}
</Typography>
</Box>
<Typography variant="caption">July 14, 2021</Typography>
</Box>
);
}
export default function ArticleCardSize6({
article,
index,
focusedCardIndex,
onSelectArticle,
onFocus,
onBlur,
}: ArticleCardProps) {
return (
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(index)}
onFocus={() => onFocus(index)}
onBlur={onBlur}
tabIndex={0}
className={focusedCardIndex === index ? 'Mui-focused' : ''}
>
<CardMedia
component="img"
alt={article.title}
image={article.img}
sx={{
aspectRatio: '16 / 9',
borderBottom: '1px solid',
borderColor: 'divider',
}}
/>
<StyledCardContent>
<Typography gutterBottom variant="caption" component="div">
{article.tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{article.title}
</Typography>
<StyledTypography variant="body2" color="text.secondary" gutterBottom>
{article.description}
</StyledTypography>
</StyledCardContent>
<Author authors={article.authors} />
</StyledCard>
);
};

View File

@@ -17,6 +17,9 @@ import SearchRoundedIcon from '@mui/icons-material/SearchRounded';
import RssFeedRoundedIcon from '@mui/icons-material/RssFeedRounded'; import RssFeedRoundedIcon from '@mui/icons-material/RssFeedRounded';
import { ArticleModel } from "../types/models"; import { ArticleModel } from "../types/models";
import ArticleCardSize6 from "./ArticleCards/ArticleCardSize6";
import ArticleCardSize4 from "./ArticleCards/ArticleCardSize4";
import ArticleCardSize2 from "./ArticleCards/ArticleCardSize2";
const StyledCard = styled(Card)(({ theme }) => ({ const StyledCard = styled(Card)(({ theme }) => ({
@@ -274,212 +277,66 @@ export default function MainContent({
</Box> </Box>
<Grid container spacing={2} columns={12}> <Grid container spacing={2} columns={12}>
<Grid size={{ xs: 12, md: 6 }}> <Grid size={{ xs: 12, md: 6 }}>
<StyledCard <ArticleCardSize6
variant="outlined" article={visibleArticles[0]}
onClick={() => onSelectArticle(0)} index={0}
onFocus={() => handleFocus(0)} focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}
tabIndex={0} />
className={focusedCardIndex === 0 ? 'Mui-focused' : ''}
>
<CardMedia
component="img"
alt="green iguana"
image={visibleArticles[0].img}
sx={{
aspectRatio: '16 / 9',
borderBottom: '1px solid',
borderColor: 'divider',
}}
/>
<StyledCardContent>
<Typography gutterBottom variant="caption" component="div">
{visibleArticles[0].tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{visibleArticles[0].title}
</Typography>
<StyledTypography variant="body2" color="text.secondary" gutterBottom>
{visibleArticles[0].description}
</StyledTypography>
</StyledCardContent>
<Author authors={visibleArticles[0].authors} />
</StyledCard>
</Grid> </Grid>
<Grid size={{ xs: 12, md: 6 }}> <Grid size={{ xs: 12, md: 6 }}>
<StyledCard <ArticleCardSize6
variant="outlined" article={visibleArticles[1]}
onClick={() => onSelectArticle(1)} index={1}
onFocus={() => handleFocus(1)} focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}
tabIndex={0} />
className={focusedCardIndex === 1 ? 'Mui-focused' : ''}
>
<CardMedia
component="img"
alt="green iguana"
image={visibleArticles[1].img}
aspect-ratio="16 / 9"
sx={{
borderBottom: '1px solid',
borderColor: 'divider',
}}
/>
<StyledCardContent>
<Typography gutterBottom variant="caption" component="div">
{visibleArticles[1].tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{visibleArticles[1].title}
</Typography>
<StyledTypography variant="body2" color="text.secondary" gutterBottom>
{visibleArticles[1].description}
</StyledTypography>
</StyledCardContent>
<Author authors={visibleArticles[1].authors} />
</StyledCard>
</Grid> </Grid>
<Grid size={{ xs: 12, md: 4 }}> <Grid size={{ xs: 12, md: 4 }}>
<StyledCard <ArticleCardSize4
variant="outlined" article={visibleArticles[2]}
onClick={() => onSelectArticle(2)} index={2}
onFocus={() => handleFocus(2)} focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}
tabIndex={0} />
className={focusedCardIndex === 2 ? 'Mui-focused' : ''}
sx={{ height: '100%' }}
>
<CardMedia
component="img"
alt="green iguana"
image={visibleArticles[2].img}
sx={{
height: { sm: 'auto', md: '50%' },
aspectRatio: { sm: '16 / 9', md: '' },
}}
/>
<StyledCardContent>
<Typography gutterBottom variant="caption" component="div">
{visibleArticles[2].tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{visibleArticles[2].title}
</Typography>
<StyledTypography variant="body2" color="text.secondary" gutterBottom>
{visibleArticles[2].description}
</StyledTypography>
</StyledCardContent>
<Author authors={visibleArticles[2].authors} />
</StyledCard>
</Grid> </Grid>
<Grid size={{ xs: 12, md: 4 }}> <Grid size={{ xs: 12, md: 4 }}>
<Box <Box
sx={{ display: 'flex', flexDirection: 'column', gap: 2, height: '100%' }} sx={{ display: 'flex', flexDirection: 'column', gap: 2, height: '100%' }}
> >
<StyledCard <ArticleCardSize2
variant="outlined" article={visibleArticles[3]}
onClick={() => onSelectArticle(3)} index={3}
onFocus={() => handleFocus(3)} focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}
tabIndex={0} />
className={focusedCardIndex === 3 ? 'Mui-focused' : ''} <ArticleCardSize2
sx={{ height: '100%' }} article={visibleArticles[4]}
> index={4}
<StyledCardContent focusedCardIndex={focusedCardIndex}
sx={{ onSelectArticle={onSelectArticle}
display: 'flex', onFocus={handleFocus}
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%',
}}
>
<div>
<Typography gutterBottom variant="caption" component="div">
{visibleArticles[3].tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{visibleArticles[3].title}
</Typography>
<StyledTypography
variant="body2"
color="text.secondary"
gutterBottom
>
{visibleArticles[3].description}
</StyledTypography>
</div>
</StyledCardContent>
<Author authors={visibleArticles[3].authors} />
</StyledCard>
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(4)}
onFocus={() => handleFocus(4)}
onBlur={handleBlur} onBlur={handleBlur}
tabIndex={0} />
className={focusedCardIndex === 4 ? 'Mui-focused' : ''}
sx={{ height: '100%' }}
>
<StyledCardContent
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
height: '100%',
}}
>
<div>
<Typography gutterBottom variant="caption" component="div">
{visibleArticles[4].tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{visibleArticles[4].title}
</Typography>
<StyledTypography
variant="body2"
color="text.secondary"
gutterBottom
>
{visibleArticles[4].description}
</StyledTypography>
</div>
</StyledCardContent>
<Author authors={visibleArticles[4].authors} />
</StyledCard>
</Box> </Box>
</Grid> </Grid>
<Grid size={{ xs: 12, md: 4 }}> <Grid size={{ xs: 12, md: 4 }}>
<StyledCard <ArticleCardSize4
variant="outlined" article={visibleArticles[5]}
onClick={() => onSelectArticle(5)} index={5}
onFocus={() => handleFocus(5)} focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}
tabIndex={0} />
className={focusedCardIndex === 5 ? 'Mui-focused' : ''}
sx={{ height: '100%' }}
>
<CardMedia
component="img"
alt="green iguana"
image={visibleArticles[5].img}
sx={{
height: { sm: 'auto', md: '50%' },
aspectRatio: { sm: '16 / 9', md: '' },
}}
/>
<StyledCardContent>
<Typography gutterBottom variant="caption" component="div">
{visibleArticles[5].tag}
</Typography>
<Typography gutterBottom variant="h6" component="div">
{visibleArticles[5].title}
</Typography>
<StyledTypography variant="body2" color="text.secondary" gutterBottom>
{visibleArticles[5].description}
</StyledTypography>
</StyledCardContent>
<Author authors={visibleArticles[5].authors} />
</StyledCard>
</Grid> </Grid>
</Grid> </Grid>
</Box> </Box>

View File

@@ -1,4 +1,6 @@
import { ArticleModel } from "./models"; import { ArticleModel } from "./models";
import {styled} from "@mui/material/styles";
import Card from "@mui/material/Card";
export interface LatestProps { export interface LatestProps {
articles: ArticleModel[]; articles: ArticleModel[];
@@ -10,3 +12,12 @@ export interface ArticleProps {
article: ArticleModel; article: ArticleModel;
onBack: () => void; onBack: () => void;
} }
export interface ArticleCardProps {
article: ArticleModel;
index: number;
focusedCardIndex: number | null;
onSelectArticle: (index: number) => void;
onFocus: (index: number) => void;
onBlur: () => void;
}