Skip to content

Commit

Permalink
Merge branch 'release/1.0.0' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
SaizFerri committed Aug 3, 2021
2 parents 7e43d41 + 0a9fbc5 commit a183d25
Show file tree
Hide file tree
Showing 66 changed files with 1,336 additions and 264 deletions.
45 changes: 7 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,10 @@
# TypeScript Next.js example
## [viktoriaweizel.com](https://viktoriaweizel.com)

This is a really simple project that shows the usage of Next.js with TypeScript.
> 🛠️ WORK IN PROGRESS
## Deploy your own
This website consists of a blog and multiple image galleries to showcase the work and adventures of Viktoria Weizel.

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-typescript&project-name=with-typescript&repository-name=with-typescript)

## How to use it?

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:

```bash
npx create-next-app --example with-typescript with-typescript-app
# or
yarn create next-app --example with-typescript with-typescript-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

## Notes

This example shows how to integrate the TypeScript type system into Next.js. Since TypeScript is supported out of the box with Next.js, all we have to do is to install TypeScript.

```
npm install --save-dev typescript
```

To enable TypeScript's features, we install the type declarations for React and Node.

```
npm install --save-dev @types/react @types/react-dom @types/node
```

When we run `next dev` the next time, Next.js will start looking for any `.ts` or `.tsx` files in our project and builds it. It even automatically creates a `tsconfig.json` file for our project with the recommended settings.

Next.js has built-in TypeScript declarations, so we'll get autocompletion for Next.js' modules straight away.

A `type-check` script is also added to `package.json`, which runs TypeScript's `tsc` CLI in `noEmit` mode to run type-checking separately. You can then include this, for example, in your `test` scripts.
#### Stack
* Next.js with Typescript (Incremental Static Site Generation)
* GraphQL
* Headless CMS Directus v9
19 changes: 19 additions & 0 deletions components/ArrowRightSmall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";
import { FunctionComponent } from "react";

const ArrowRightSmall: FunctionComponent = () => (
<svg
width="26"
height="16"
viewBox="0 0 26 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M25.7071 8.70711C26.0976 8.31658 26.0976 7.68342 25.7071 7.29289L19.3431 0.928932C18.9526 0.538408 18.3195 0.538408 17.9289 0.928932C17.5384 1.31946 17.5384 1.95262 17.9289 2.34315L23.5858 8L17.9289 13.6569C17.5384 14.0474 17.5384 14.6805 17.9289 15.0711C18.3195 15.4616 18.9526 15.4616 19.3431 15.0711L25.7071 8.70711ZM0 9L25 9V7L0 7L0 9Z"
fill="white"
/>
</svg>
);

export default ArrowRightSmall;
16 changes: 15 additions & 1 deletion components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Link from "next/link";
import { FunctionComponent } from "react";
import { FaInstagram } from "react-icons/fa";

Expand All @@ -7,7 +8,20 @@ const Footer: FunctionComponent = () => {
return (
<footer className="footer">
<div className="footer__content">
<small>&#169;&nbsp;Copyright {year} Viktoria Weizel</small>
<div className="footer__legal">
<small>&#169;&nbsp;Copyright {year} Viktoria Weizel&nbsp;| </small>
<Link href="/impressum">
<a>
<small>Impressum</small>
</a>
</Link>
<small>&nbsp;|&nbsp;</small>
<Link href="/privacy-policy">
<a>
<small>Privacy Policy</small>
</a>
</Link>
</div>
<ul className="footer__social-list">
<li className="footer__social-list-item">
<a
Expand Down
28 changes: 28 additions & 0 deletions components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Link from "next/link";
import React, { FunctionComponent } from "react";

const Hero: FunctionComponent<{}> = () => {
return (
<div className="hero">
<div className="container">
<div className="row">
<div className="col-lg-8 col-12">
<h1 className="hero__title">Hi, I'm Viktoria</h1>
<h3 className="hero__subtitle">
Passionate traveler <br /> & photographer.
</h3>
</div>
</div>
<div className="row">
<div className="col-12">
<Link href="/gallery">
<a className="button--white">Gallery</a>
</Link>
</div>
</div>
</div>
</div>
);
};

export default Hero;
8 changes: 6 additions & 2 deletions components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ import Footer from "./Footer";

interface ILayoutProps {
children: ReactChild | ReactChild[];
classNames?: string;
}

const Layout: FunctionComponent<ILayoutProps> = ({ children }) => {
const Layout: FunctionComponent<ILayoutProps> = ({
children,
classNames = "",
}) => {
return (
<>
<header>
<Navbar />
</header>
<main>{children}</main>
<main className={classNames}>{children}</main>
<Footer />
</>
);
Expand Down
25 changes: 25 additions & 0 deletions components/LinkWithIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Link from "next/link";
import React, { FunctionComponent } from "react";

interface ILinkWithIconProps {
title: string;
href: string;
children: React.ReactElement | React.ReactElement[];
}

const LinkWithIcon: FunctionComponent<ILinkWithIconProps> = ({
title,
href,
children,
}): React.ReactElement => {
return (
<Link href={href}>
<a className="link link--icon">
<span className="link__title">{title}</span>
{children}
</a>
</Link>
);
};

export default LinkWithIcon;
54 changes: 51 additions & 3 deletions components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,57 @@
import { FunctionComponent } from "react";
import { useState, useEffect, FunctionComponent } from "react";
import Link from "next/link";
import { useTheme } from "./ThemeProvider";
import ETheme from "../enums/theme.enum";
import ThemeToggler from "./ThemeToggler";
import { useRouter } from "next/router";

const Navbar: FunctionComponent = () => {
const [isNotTop, setIsNotTop] = useState(false);
const { theme, setTheme } = useTheme();
const router = useRouter();

const isHome = router.route === "/";

const changeTheme = (e) => {
if (e.target.checked) {
setTheme(ETheme.DARK);
return;
}

setTheme(ETheme.LIGHT);
};

const handleScroll = () => {
if (window.scrollY > 0) {
setIsNotTop(() => true);
return;
}

setIsNotTop(() => false)
}

useEffect(() => {
window.addEventListener("scroll", handleScroll);

return () => {
window.removeEventListener("scroll", handleScroll);
}
}, []);

return (
<nav className="navbar">
<nav className={`navbar ${isHome ? "ignore-theme" : ""} ${isNotTop ? "has-background" : ""}`}>
<div className="navbar__content">
<div className="navbar__logo">
<Link href="/">
<a>
<img src="/images/logo_svg.svg" alt="Logo" />
<img
src={`${
theme === ETheme.LIGHT && !isHome
? "/images/logo.svg"
: "/images/logo_nightmode.svg"
}`}
alt="Logo"
/>
</a>
</Link>
</div>
Expand All @@ -23,6 +66,11 @@ const Navbar: FunctionComponent = () => {
<a>Blog</a>
</Link>
</li>
{!isHome && (
<li className="navbar__menu-item">
<ThemeToggler onToggle={changeTheme} />
</li>
)}
</ul>
</div>
</nav>
Expand Down
33 changes: 33 additions & 0 deletions components/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { createContext, useState, useEffect, useContext } from "react";
import ETheme from "../enums/theme.enum";

interface IThemeProvider {
theme: ETheme;
setTheme: (theme: ETheme) => void;
}

export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState(ETheme.DARK);

useEffect(() => {
const bodyClassList = document.body.classList;
if (theme === ETheme.LIGHT) {
bodyClassList.remove("dark-theme");
bodyClassList.add("light-theme");
return;
}

bodyClassList.remove("light-theme");
bodyClassList.add("dark-theme");
}, [theme]);

return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};

export const ThemeContext = createContext<Partial<IThemeProvider>>({});
export const useTheme = (): IThemeProvider =>
useContext(ThemeContext) as IThemeProvider;
30 changes: 30 additions & 0 deletions components/ThemeToggler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";
import { FaMoon } from "react-icons/fa";
import { FaSun } from "react-icons/fa";
import ETheme from "../enums/theme.enum";
import { useTheme } from "./ThemeProvider";

const ThemeToggler = ({ onToggle }) => {
const { theme } = useTheme();
return (
<>
<input
type="checkbox"
onChange={onToggle}
name="chooseTheme"
id="chooseTheme"
className="d-none"
checked={theme === ETheme.DARK}
/>
<label htmlFor="chooseTheme">
{theme === ETheme.LIGHT ? (
<FaMoon />
) : (
<FaSun style={{ color: "var(--white)" }} />
)}
</label>
</>
);
};

export default ThemeToggler;
66 changes: 20 additions & 46 deletions components/blog/BlogCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { FunctionComponent } from "react";
import Link from "next/link";
import { format } from "date-fns";
import { de } from "date-fns/locale";
import Card from "../cards/Card";
import Image from "../Image";
import DIRECTUS_URL from "../../consts/directusBaseUrl";

interface IBlogCardProps {
thumbnail: Record<string, string | number>;
Expand All @@ -20,52 +19,27 @@ const BlogCard: FunctionComponent<IBlogCardProps> = ({
subtitle,
slug,
}) => {
const CardImage = () => {
return (
<Link href={`/blog/${slug}`}>
<a>
<div className="aspect-ratio-4x3 aspect-ratio--cover">
<Image image={thumbnail} alt={"thumbnail.title"} />
</div>
</a>
</Link>
);
};

const CardBody = () => {
const formatedDate = format(new Date(createdOn), "dd MMMM yyyy", {
locale: de,
});
return (
<>
<div className="blog-card__title">
<Link href={`/blog/${slug}`}>
<a className="clean d-block">
<h3 className="card__title">{title}</h3>
</a>
</Link>
<small className="blog-card__date">{formatedDate}</small>
</div>
<p className="card__teaser">{subtitle}</p>
</>
);
};

const CardFooter = () => (
<>
<Link href={`/blog/${slug}`}>
<a className="button">Read more</a>
</Link>
</>
);
const backgroundImage = `
linear-gradient(180deg, rgba(26, 26, 26, 0) 0%, rgba(26, 26, 26, 0.62) 49.48%, #1A1A1A 100%),
url(${DIRECTUS_URL}/assets/${thumbnail.id})
`;
const formatedDate = format(new Date(createdOn), "dd MMMM yyyy", {
locale: de,
});

return (
<Card
classNames="blog-card"
image={<CardImage />}
body={<CardBody />}
footer={<CardFooter />}
/>
<Link href={`/blog/${slug}`}>
<div
className="blog-card"
style={{
backgroundImage: backgroundImage,
}}
>
<small className="blog-card__date">{formatedDate}</small>
<h4 className="blog-card__title">{title}</h4>
<p className="card__subtitle">{subtitle}</p>
</div>
</Link>
);
};

Expand Down
Loading

1 comment on commit a183d25

@vercel
Copy link

@vercel vercel bot commented on a183d25 Aug 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.