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 { ArticleModel } from "../types/models";
import ArticleCardSize6 from "./ArticleCards/ArticleCardSize6";
import ArticleCardSize4 from "./ArticleCards/ArticleCardSize4";
import ArticleCardSize2 from "./ArticleCards/ArticleCardSize2";
const StyledCard = styled(Card)(({ theme }) => ({
@@ -274,212 +277,66 @@ export default function MainContent({
</Box>
<Grid container spacing={2} columns={12}>
<Grid size={{ xs: 12, md: 6 }}>
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(0)}
onFocus={() => handleFocus(0)}
<ArticleCardSize6
article={visibleArticles[0]}
index={0}
focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
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 size={{ xs: 12, md: 6 }}>
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(1)}
onFocus={() => handleFocus(1)}
<ArticleCardSize6
article={visibleArticles[1]}
index={1}
focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
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 size={{ xs: 12, md: 4 }}>
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(2)}
onFocus={() => handleFocus(2)}
<ArticleCardSize4
article={visibleArticles[2]}
index={2}
focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
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 size={{ xs: 12, md: 4 }}>
<Box
sx={{ display: 'flex', flexDirection: 'column', gap: 2, height: '100%' }}
>
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(3)}
onFocus={() => handleFocus(3)}
<ArticleCardSize2
article={visibleArticles[3]}
index={3}
focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
onBlur={handleBlur}
tabIndex={0}
className={focusedCardIndex === 3 ? 'Mui-focused' : ''}
sx={{ height: '100%' }}
>
<StyledCardContent
sx={{
display: 'flex',
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)}
/>
<ArticleCardSize2
article={visibleArticles[4]}
index={4}
focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
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>
</Grid>
<Grid size={{ xs: 12, md: 4 }}>
<StyledCard
variant="outlined"
onClick={() => onSelectArticle(5)}
onFocus={() => handleFocus(5)}
<ArticleCardSize4
article={visibleArticles[5]}
index={5}
focusedCardIndex={focusedCardIndex}
onSelectArticle={onSelectArticle}
onFocus={handleFocus}
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>
</Box>

View File

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