2024-03-31 21:19:04 +08:00
|
|
|
import React, { useContext } from 'react';
|
2024-07-27 16:13:25 +08:00
|
|
|
import { Badge, Carousel, Flex, Skeleton, Typography } from 'antd';
|
|
|
|
import { createStyles } from 'antd-style';
|
2023-11-29 12:32:15 +08:00
|
|
|
import classNames from 'classnames';
|
|
|
|
|
|
|
|
import useLocale from '../../../hooks/useLocale';
|
2022-12-19 11:42:41 +08:00
|
|
|
import SiteContext from '../../../theme/slots/SiteContext';
|
2023-11-29 12:32:15 +08:00
|
|
|
import type { Extra, Icon } from './util';
|
2023-07-24 16:59:35 +08:00
|
|
|
import { getCarouselStyle, useSiteData } from './util';
|
2022-11-09 12:28:04 +08:00
|
|
|
|
2023-11-29 19:05:19 +08:00
|
|
|
const useStyle = createStyles(({ token, css, cx }) => {
|
2023-07-20 19:27:33 +08:00
|
|
|
const { carousel } = getCarouselStyle();
|
2023-11-29 19:05:19 +08:00
|
|
|
|
|
|
|
const itemBase = css`
|
|
|
|
display: flex;
|
|
|
|
flex: 1 1 0;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: stretch;
|
|
|
|
text-decoration: none;
|
|
|
|
background: ${token.colorBgContainer};
|
|
|
|
border: ${token.lineWidth}px solid ${token.colorBorderSecondary};
|
|
|
|
border-radius: ${token.borderRadiusLG}px;
|
|
|
|
transition: all ${token.motionDurationSlow};
|
|
|
|
padding-block: ${token.paddingMD}px;
|
|
|
|
padding-inline: ${token.paddingLG}px;
|
|
|
|
box-sizing: border-box;
|
|
|
|
`;
|
|
|
|
|
2022-11-09 12:28:04 +08:00
|
|
|
return {
|
2023-11-29 19:05:19 +08:00
|
|
|
itemBase,
|
|
|
|
ribbon: css`
|
|
|
|
& > .${cx(itemBase)} {
|
|
|
|
height: 100%;
|
|
|
|
}
|
2022-12-11 20:39:46 +08:00
|
|
|
`,
|
|
|
|
cardItem: css`
|
2022-11-09 12:28:04 +08:00
|
|
|
&:hover {
|
|
|
|
box-shadow: ${token.boxShadowCard};
|
|
|
|
}
|
|
|
|
`,
|
2022-12-11 20:39:46 +08:00
|
|
|
sliderItem: css`
|
|
|
|
margin: 0 ${token.margin}px;
|
|
|
|
text-align: start;
|
|
|
|
`,
|
|
|
|
container: css`
|
|
|
|
display: flex;
|
2024-03-31 21:19:04 +08:00
|
|
|
width: 100%;
|
|
|
|
max-width: 100%;
|
2022-12-11 20:39:46 +08:00
|
|
|
margin-inline: auto;
|
|
|
|
box-sizing: border-box;
|
|
|
|
column-gap: ${token.paddingMD * 2}px;
|
|
|
|
align-items: stretch;
|
|
|
|
text-align: start;
|
2024-02-25 21:11:04 +08:00
|
|
|
min-height: 178px;
|
2023-11-28 15:31:36 +08:00
|
|
|
> * {
|
|
|
|
width: calc((100% - ${token.marginXXL * 2}px) / 3);
|
|
|
|
}
|
2022-12-11 20:39:46 +08:00
|
|
|
`,
|
|
|
|
carousel,
|
2024-07-27 16:13:25 +08:00
|
|
|
bannerBg: css`
|
|
|
|
height: ${token.fontSize}px;
|
|
|
|
`,
|
2022-11-09 12:28:04 +08:00
|
|
|
};
|
2023-07-20 19:27:33 +08:00
|
|
|
});
|
2022-11-09 12:28:04 +08:00
|
|
|
|
2022-12-11 20:39:46 +08:00
|
|
|
interface RecommendItemProps {
|
|
|
|
extra: Extra;
|
|
|
|
index: number;
|
|
|
|
icons: Icon[];
|
2023-07-20 19:27:33 +08:00
|
|
|
className?: string;
|
2022-12-11 20:39:46 +08:00
|
|
|
}
|
2024-07-27 16:13:25 +08:00
|
|
|
|
2023-11-29 12:32:15 +08:00
|
|
|
const RecommendItem: React.FC<RecommendItemProps> = ({ extra, index, icons, className }) => {
|
2023-07-20 19:27:33 +08:00
|
|
|
const { styles } = useStyle();
|
2022-12-11 20:39:46 +08:00
|
|
|
|
|
|
|
if (!extra) {
|
|
|
|
return <Skeleton key={index} />;
|
|
|
|
}
|
|
|
|
const icon = icons.find((i) => i.name === extra.source);
|
|
|
|
|
2023-11-28 15:31:36 +08:00
|
|
|
const card = (
|
2022-12-19 11:42:41 +08:00
|
|
|
<a
|
|
|
|
key={extra?.title}
|
|
|
|
href={extra.href}
|
|
|
|
target="_blank"
|
2023-11-29 19:05:19 +08:00
|
|
|
className={classNames(styles.itemBase, className)}
|
2022-12-19 11:42:41 +08:00
|
|
|
rel="noreferrer"
|
|
|
|
>
|
2022-12-11 20:39:46 +08:00
|
|
|
<Typography.Title level={5}>{extra?.title}</Typography.Title>
|
|
|
|
<Typography.Paragraph type="secondary" style={{ flex: 'auto' }}>
|
|
|
|
{extra.description}
|
|
|
|
</Typography.Paragraph>
|
2024-07-27 16:13:25 +08:00
|
|
|
<Flex justify="space-between" align="center">
|
2022-12-11 20:39:46 +08:00
|
|
|
<Typography.Text>{extra.date}</Typography.Text>
|
2024-07-27 16:13:25 +08:00
|
|
|
{icon && <img src={icon.href} draggable={false} className={styles.bannerBg} alt="banner" />}
|
|
|
|
</Flex>
|
2022-12-11 20:39:46 +08:00
|
|
|
</a>
|
|
|
|
);
|
2023-11-28 15:31:36 +08:00
|
|
|
|
|
|
|
if (index === 0) {
|
|
|
|
return (
|
2023-11-29 19:05:19 +08:00
|
|
|
<Badge.Ribbon text="HOT" color="red" rootClassName={styles.ribbon}>
|
2023-11-28 15:31:36 +08:00
|
|
|
{card}
|
|
|
|
</Badge.Ribbon>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return card;
|
2022-12-11 20:39:46 +08:00
|
|
|
};
|
|
|
|
|
2024-03-31 21:19:04 +08:00
|
|
|
export const BannerRecommendsFallback: React.FC = () => {
|
2023-07-24 16:59:35 +08:00
|
|
|
const { isMobile } = useContext(SiteContext);
|
|
|
|
const { styles } = useStyle();
|
|
|
|
|
2024-09-19 03:30:19 +08:00
|
|
|
const list = new Array(3).fill(1);
|
2023-07-24 16:59:35 +08:00
|
|
|
|
|
|
|
return isMobile ? (
|
|
|
|
<Carousel className={styles.carousel}>
|
2023-11-28 15:31:36 +08:00
|
|
|
{list.map((_, index) => (
|
2023-12-04 15:12:49 +08:00
|
|
|
<div key={index} className={styles.itemBase}>
|
2023-07-24 16:59:35 +08:00
|
|
|
<Skeleton active style={{ padding: '0 24px' }} />
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</Carousel>
|
|
|
|
) : (
|
|
|
|
<div className={styles.container}>
|
|
|
|
{list.map((_, index) => (
|
2023-12-04 15:12:49 +08:00
|
|
|
<div key={index} className={styles.itemBase}>
|
|
|
|
<Skeleton active />
|
|
|
|
</div>
|
2023-07-24 16:59:35 +08:00
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
2022-11-09 12:28:04 +08:00
|
|
|
|
2023-11-29 12:32:15 +08:00
|
|
|
const BannerRecommends: React.FC = () => {
|
2023-07-20 19:27:33 +08:00
|
|
|
const { styles } = useStyle();
|
2023-07-24 16:59:35 +08:00
|
|
|
const [, lang] = useLocale();
|
2022-12-11 20:39:46 +08:00
|
|
|
const { isMobile } = React.useContext(SiteContext);
|
2023-07-24 16:59:35 +08:00
|
|
|
const data = useSiteData();
|
|
|
|
const extras = data?.extras?.[lang];
|
2023-11-28 15:31:36 +08:00
|
|
|
const icons = data?.icons || [];
|
2024-09-19 03:30:19 +08:00
|
|
|
const first3 = !extras || extras.length === 0 ? new Array(3).fill(null) : extras.slice(0, 3);
|
2022-11-09 12:28:04 +08:00
|
|
|
|
2023-12-04 15:12:49 +08:00
|
|
|
if (!data) {
|
|
|
|
return <BannerRecommendsFallback />;
|
|
|
|
}
|
|
|
|
|
2024-03-31 21:19:04 +08:00
|
|
|
if (isMobile) {
|
|
|
|
return (
|
|
|
|
<Carousel className={styles.carousel}>
|
|
|
|
{first3.map((extra, index) => (
|
|
|
|
<div key={index}>
|
2022-12-11 20:39:46 +08:00
|
|
|
<RecommendItem
|
|
|
|
extra={extra}
|
|
|
|
index={index}
|
|
|
|
icons={icons}
|
2024-03-31 21:19:04 +08:00
|
|
|
className={styles.sliderItem}
|
2022-12-11 20:39:46 +08:00
|
|
|
/>
|
2024-03-31 21:19:04 +08:00
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</Carousel>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={styles.container}>
|
|
|
|
{first3.map((extra, index) => (
|
|
|
|
<RecommendItem
|
|
|
|
extra={extra}
|
|
|
|
index={index}
|
|
|
|
icons={icons}
|
|
|
|
className={styles.cardItem}
|
|
|
|
key={index}
|
|
|
|
/>
|
|
|
|
))}
|
2022-11-09 12:28:04 +08:00
|
|
|
</div>
|
|
|
|
);
|
2023-11-29 12:32:15 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
export default BannerRecommends;
|