mirror of
https://github.com/morten-olsen/morten-olsen.github.io.git
synced 2026-02-08 01:46:28 +01:00
init
This commit is contained in:
77
content/templates/react/components/article/grid/index.tsx
Normal file
77
content/templates/react/components/article/grid/index.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React, { useMemo } from "react";
|
||||
import styled from "styled-components";
|
||||
import ArticlePreview from "../preview";
|
||||
import { JumboArticlePreview } from "../preview/jumbo";
|
||||
import { MiniArticlePreview } from "../preview/mini";
|
||||
import { Article } from "types";
|
||||
|
||||
type Props = {
|
||||
articles: Article[];
|
||||
};
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const FeaturedArticle = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const FeaturedArticles = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const RemainingArticles = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const ArticleGrid: React.FC<Props> = ({ articles }) => {
|
||||
const sorted = useMemo(
|
||||
() => articles,
|
||||
// TODO:
|
||||
// articles.sort(
|
||||
// (a, b) =>
|
||||
// new Date(b.published).getTime() -
|
||||
// new Date(a.published).getTime()
|
||||
// ),
|
||||
[articles]
|
||||
);
|
||||
const featured1 = useMemo(() => sorted.slice(0, 1)[0], [sorted]);
|
||||
|
||||
const featured2 = useMemo(() => sorted.slice(1, 4), [sorted]);
|
||||
|
||||
const remaining = useMemo(() => sorted.slice(4, 12), [sorted]);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<FeaturedArticle>
|
||||
<JumboArticlePreview article={featured1} />
|
||||
</FeaturedArticle>
|
||||
<FeaturedArticles>
|
||||
{featured2.map((article) => (
|
||||
<ArticlePreview key={article.title} article={article} />
|
||||
))}
|
||||
</FeaturedArticles>
|
||||
<RemainingArticles>
|
||||
{remaining.map((article) => (
|
||||
<MiniArticlePreview key={article.title} article={article} />
|
||||
))}
|
||||
</RemainingArticles>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export { ArticleGrid };
|
||||
82
content/templates/react/components/article/preview/index.tsx
Normal file
82
content/templates/react/components/article/preview/index.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import React, { useMemo } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Title1 } from "@/typography";
|
||||
import { createTheme } from "@/theme/create";
|
||||
import { ThemeProvider } from "@/theme/provider";
|
||||
import { Article } from "types";
|
||||
|
||||
type Props = {
|
||||
article: Article;
|
||||
};
|
||||
|
||||
const Wrapper = styled.a`
|
||||
height: 500px;
|
||||
border-right: 2px solid rgba(0, 0, 0, 0.1);
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
position: relative;
|
||||
margin: 15px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
max-height: 300px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Title = styled(Title1)`
|
||||
background: ${({ theme }) => theme.colors.primary};
|
||||
line-height: 40px;
|
||||
font-family: "Black Ops One", sans-serif;
|
||||
font-size: 25px;
|
||||
padding: 0 5px;
|
||||
margin: 5px 0;
|
||||
`;
|
||||
|
||||
const MetaWrapper = styled.div`
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const AsideWrapper = styled.aside<{
|
||||
image?: string;
|
||||
}>`
|
||||
background: ${({ theme }) => theme.colors.primary};
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
${({ image }) => (image ? `background-image: url(${image});` : "")}
|
||||
flex: 1;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ArticlePreview: React.FC<Props> = ({ article }) => {
|
||||
const theme = useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
baseColor: article.color,
|
||||
}),
|
||||
[article.color]
|
||||
);
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Wrapper href={`/articles/${article.slug}`}>
|
||||
<AsideWrapper image={article.thumbUrl} />
|
||||
<MetaWrapper>
|
||||
{article.title.split(" ").map((word, index) => (
|
||||
<Title key={index}>{word}</Title>
|
||||
))}
|
||||
</MetaWrapper>
|
||||
</Wrapper>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArticlePreview;
|
||||
79
content/templates/react/components/article/preview/jumbo.tsx
Normal file
79
content/templates/react/components/article/preview/jumbo.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Title1, Body1 } from "@/typography";
|
||||
import { Article } from "types";
|
||||
|
||||
type Props = {
|
||||
article: Article;
|
||||
};
|
||||
|
||||
const Wrapper = styled.a`
|
||||
height: 300px;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
margin: 15px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
background: ${({ theme }) => theme.colors.background};
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
flex-direction: column;
|
||||
height: 500px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Title = styled(Title1)`
|
||||
line-height: 40px;
|
||||
font-family: "Black Ops One", sans-serif;
|
||||
font-size: 25px;
|
||||
padding: 0 5px;
|
||||
margin: 5px 0;
|
||||
`;
|
||||
|
||||
const Summery = styled(Body1)`
|
||||
max-width: 300px;
|
||||
padding: 0 5px;
|
||||
margin: 5px 0;
|
||||
overflow: hidden;
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 2.1rem;
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
max-height: 100px;
|
||||
}
|
||||
`;
|
||||
|
||||
const MetaWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 40px;
|
||||
`;
|
||||
|
||||
const AsideWrapper = styled.aside<{
|
||||
image?: string;
|
||||
}>`
|
||||
background: ${({ theme }) => theme.colors.primary};
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
${({ image }) => (image ? `background-image: url(${image});` : "")}
|
||||
flex: 1;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const JumboArticlePreview: React.FC<Props> = ({ article }) => {
|
||||
return (
|
||||
<Wrapper href={`/articles/${article.slug}`}>
|
||||
<AsideWrapper image={article.coverUrl} />
|
||||
<MetaWrapper>
|
||||
<Title>{article.title}</Title>
|
||||
<Summery>{article.content}</Summery>
|
||||
</MetaWrapper>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export { JumboArticlePreview };
|
||||
80
content/templates/react/components/article/preview/mini.tsx
Normal file
80
content/templates/react/components/article/preview/mini.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import React, { useMemo } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Title1 } from "@/typography";
|
||||
import { createTheme } from "@/theme/create";
|
||||
import { ThemeProvider } from "@/theme/provider";
|
||||
import { Article } from "types";
|
||||
|
||||
type Props = {
|
||||
article: Article;
|
||||
};
|
||||
|
||||
const Wrapper = styled.a`
|
||||
position: relative;
|
||||
margin: 15px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: 220px;
|
||||
height: 200px;
|
||||
|
||||
@media only screen and (max-width: 700px) {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const Title = styled(Title1)`
|
||||
line-height: 20px;
|
||||
font-size: 20px;
|
||||
padding: 5px 5px;
|
||||
font-family: "Black Ops One", sans-serif;
|
||||
margin: 5px 0;
|
||||
background: ${({ theme }) => theme.colors.background};
|
||||
`;
|
||||
|
||||
const MetaWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
padding: 10px;
|
||||
max-width: 220px;
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
const AsideWrapper = styled.aside<{
|
||||
image?: string;
|
||||
}>`
|
||||
background: ${({ theme }) => theme.colors.primary};
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
${({ image }) => (image ? `background-image: url(${image});` : "")}
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const MiniArticlePreview: React.FC<Props> = ({ article }) => {
|
||||
const theme = useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
baseColor: article.color,
|
||||
}),
|
||||
[article.color]
|
||||
);
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Wrapper href={`/articles/${article.slug}`}>
|
||||
<AsideWrapper image={article.thumbUrl} />
|
||||
<MetaWrapper>
|
||||
{article.title.split(" ").map((word, index) => (
|
||||
<Title key={index}>{word}</Title>
|
||||
))}
|
||||
</MetaWrapper>
|
||||
</Wrapper>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export { MiniArticlePreview };
|
||||
Reference in New Issue
Block a user