This commit is contained in:
Morten Olsen
2023-03-26 22:15:07 +02:00
commit 9b1a067d56
80 changed files with 7889 additions and 0 deletions

View 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 };

View 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;

View 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 };

View 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 };

View File

@@ -0,0 +1,30 @@
import { FC, ReactNode } from "react"
type HtmlProps = {
body: ReactNode;
head: ReactNode;
scripts: string[];
};
const Html: FC<HtmlProps> = ({ body, head, scripts }) => {
return (
<html>
<head>
<title>My App</title>
{head}
{scripts.map((script, index) => (
<script key={index} src={script} />
))}
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous"/>
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Black+Ops+One&family=Merriweather:wght@400;700&display=swap" rel="stylesheet" />
</head>
<body>
<div id="root">{body}</div>
</body>
</html>
);
};
export { Html };

View File

@@ -0,0 +1,65 @@
import React, { ReactNode, useMemo } from "react";
import styled from "styled-components";
import { createTheme } from "@/theme/create";
import { ThemeProvider } from "@/theme/provider";
const Wrapper = styled.div`
background: ${({ theme }) => theme.colors.background};
color: ${({ theme }) => theme.colors.foreground};
min-height: 100%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
`;
const BackgroundWrapper = styled.div<{
image?: string;
}>`
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
background-size: cover;
background-position: center;
opacity: 0.2;
${({ image }) => (image ? `background-image: url(${image});` : "")}
`;
const Content = styled.div`
z-index: 1;
display: flex;
max-width: 1000px;
width: 100%;
align-items: center;
justify-content: center;
flex-direction: column;
`;
type Props = {
children: ReactNode;
background?: string;
color?: string;
};
const Sheet: React.FC<Props> = ({ color, background, children }) => {
const theme = useMemo(
() =>
createTheme({
baseColor: color,
}),
[color]
);
return (
<ThemeProvider theme={theme}>
<Wrapper>
<BackgroundWrapper image={background} />
<Content>{children}</Content>
</Wrapper>
</ThemeProvider>
);
};
export { Sheet };