diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..65a3cf3 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +NEXT_PUBLIC_FORMSPREE_KEY=FORMSPREE_KEY +NEXT_PUBLIC_GOOGLE_ANALYTICS_KEY=GOOGLE_ANALYTICS_KEY \ No newline at end of file diff --git a/components/Card/index.tsx b/components/Card/index.tsx index 9c6e250..cf48e17 100644 --- a/components/Card/index.tsx +++ b/components/Card/index.tsx @@ -1,18 +1,8 @@ -import { FC } from 'react'; -import { StyledCard } from './styles'; import Image from 'next/image'; +import { StyledCard } from './styles'; +import { Props } from './types'; -interface Props { - title: string; - description: string; - image?: string; - tags?: string[]; - href: string; - target?: HTMLAnchorElement['target']; - onClick?: () => void; -} - -const Card: FC = ({ title, description, image, tags, href, target, onClick }) => { +const Card = ({ title, description, image, tags, href, target, onClick }: Props) => { return (
diff --git a/components/Card/types.ts b/components/Card/types.ts new file mode 100644 index 0000000..6661255 --- /dev/null +++ b/components/Card/types.ts @@ -0,0 +1,9 @@ +export interface Props { + title: string; + description: string; + image?: string; + tags?: string[]; + href: string; + target?: HTMLAnchorElement['target']; + onClick?: () => void; +} \ No newline at end of file diff --git a/components/CodeBlock/index.tsx b/components/CodeBlock/index.tsx index 4703de0..c811cfb 100644 --- a/components/CodeBlock/index.tsx +++ b/components/CodeBlock/index.tsx @@ -1,9 +1,9 @@ -import { FC } from 'react'; import Highlight, { defaultProps, Language } from 'prism-react-renderer'; import theme from 'prism-react-renderer/themes/vsDark'; +import { Props } from './types'; import { Line, LineContent, LineNo, Pre } from './styles'; -const CodeBlock: FC<{ className: string, children: React.ReactNode }> = ({ children, className }) => { +const CodeBlock = ({ children, className }: Props) => { const language = className.replace(/language-/, '') as Language; return ( diff --git a/components/CodeBlock/types.ts b/components/CodeBlock/types.ts new file mode 100644 index 0000000..6e46e87 --- /dev/null +++ b/components/CodeBlock/types.ts @@ -0,0 +1,4 @@ +export interface Props { + className: string; + children: React.ReactNode; +} \ No newline at end of file diff --git a/components/Footer/index.tsx b/components/Footer/index.tsx index fd6d59c..daa74b3 100644 --- a/components/Footer/index.tsx +++ b/components/Footer/index.tsx @@ -1,9 +1,9 @@ -import { FC, useContext } from 'react'; +import { useContext } from 'react'; import { ThemeContext } from '../../styles/theme'; import { StyledFooter } from './styles'; -import IconButton from '../../components/IconButton'; +import IconButton from '../IconButton'; -const Footer: FC = () => { +const Footer = () => { const { mode } = useContext(ThemeContext); return ( diff --git a/components/Hero/index.tsx b/components/Hero/index.tsx index 3516ffe..ad01230 100644 --- a/components/Hero/index.tsx +++ b/components/Hero/index.tsx @@ -1,8 +1,7 @@ -import { FC } from 'react'; import { Wrapper } from './styles'; import Image from 'next/image'; -const Hero: FC = () => ( +const Hero = () => (

Hi, I am Hazem

diff --git a/components/IconButton.tsx b/components/IconButton.tsx deleted file mode 100644 index ab24cc4..0000000 --- a/components/IconButton.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { FC } from 'react'; -import styled from 'styled-components'; -import Image from 'next/image'; -import Link from 'next/link'; - -interface Props { - alt: string; - icon: string; - width?: number; - height?: number; - href?: string; - target?: HTMLAnchorElement['target']; - onClick?: () => void; -} - -const Btn = styled(Link)` - cursor: pointer; - background: none; - border: none; - display: inline-flex; - align-items: center; -`; - -const IconButton: FC = ({ - alt, - icon, - href, - target, - onClick, - className, - width = 24, - height = 24 -}) => { - return !href ? ( - - {alt} - - ) : ( - - {alt} - - ); -}; - -export default IconButton; diff --git a/components/IconButton/index.tsx b/components/IconButton/index.tsx new file mode 100644 index 0000000..96792c0 --- /dev/null +++ b/components/IconButton/index.tsx @@ -0,0 +1,24 @@ +import Image from 'next/image'; +import { Props } from './types'; +import { StyledButton, StyledLink } from './styles'; + +const IconButton = ({ + alt, + icon, + href, + target, + onClick, + className, + width = 24, + height = 24 +}: Props) => href ? ( + + {alt} + + ) : ( + + {alt} + + ); + +export default IconButton; diff --git a/components/IconButton/styles.tsx b/components/IconButton/styles.tsx new file mode 100644 index 0000000..de75887 --- /dev/null +++ b/components/IconButton/styles.tsx @@ -0,0 +1,18 @@ +import styled, { css } from 'styled-components'; +import Link from 'next/link'; + +const sharedStyles = css` + cursor: pointer; + background: none; + border: none; + display: inline-flex; + align-items: center; +`; + +export const StyledLink = styled(Link)` + ${sharedStyles} +`; + +export const StyledButton = styled.button` + ${sharedStyles} +`; \ No newline at end of file diff --git a/components/IconButton/types.ts b/components/IconButton/types.ts new file mode 100644 index 0000000..3530902 --- /dev/null +++ b/components/IconButton/types.ts @@ -0,0 +1,10 @@ +export interface Props { + alt: string; + icon: string; + width?: number; + height?: number; + href?: string; + target?: HTMLAnchorElement['target']; + onClick?: () => void; + className?: string; +} \ No newline at end of file diff --git a/components/Input.tsx b/components/Input.tsx deleted file mode 100644 index 93a419a..0000000 --- a/components/Input.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { FC } from 'react'; -import styled from 'styled-components'; - -interface Props { - placeholder?: string; - type: 'text' | 'email'; - variant: 'small' | 'big'; - name: string; - value: string; - required?: boolean; - onChange?: (event: React.ChangeEvent) => void; -} - -const SmallField = styled.input` - border: none; - padding: 1rem; - background: var(--secondary-background); - color: var(--text); -`; - -const BigField = styled.textarea` - resize: none; - border: none; - padding: 1rem; - background: var(--secondary-background); - color: var(--text); -`; - -const Input: FC = ({ - type = 'text', - variant = 'small', - name, - value, - required, - placeholder, - className, - onChange -}) => { - return variant === 'small' ? ( - - ) : ( - - ); -}; - -export default Input; diff --git a/components/Input/index.tsx b/components/Input/index.tsx new file mode 100644 index 0000000..b5adaca --- /dev/null +++ b/components/Input/index.tsx @@ -0,0 +1,37 @@ +import { BigField, SmallField } from "./styles"; +import { Props } from "./types"; + +const Input = ({ + type = 'text', + variant = 'small', + name, + value, + required, + placeholder, + className, + onChange +}: Props) => { + return variant === 'small' ? ( + + ) : ( + + ); +}; + +export default Input; diff --git a/components/Input/styles.tsx b/components/Input/styles.tsx new file mode 100644 index 0000000..a18cf96 --- /dev/null +++ b/components/Input/styles.tsx @@ -0,0 +1,16 @@ +import styled from "styled-components"; + +export const SmallField = styled.input` + border: none; + padding: 1rem; + background: var(--secondary-background); + color: var(--text); +`; + +export const BigField = styled.textarea` + resize: none; + border: none; + padding: 1rem; + background: var(--secondary-background); + color: var(--text); +`; \ No newline at end of file diff --git a/components/Input/types.ts b/components/Input/types.ts new file mode 100644 index 0000000..3709223 --- /dev/null +++ b/components/Input/types.ts @@ -0,0 +1,10 @@ +export interface Props { + placeholder?: string; + type: 'text' | 'email'; + variant: 'small' | 'big'; + name: string; + value: string; + required?: boolean; + onChange?: (event: React.ChangeEvent) => void; + className?: string; +} diff --git a/components/MDXButton/index.tsx b/components/MDXButton/index.tsx index be7fce6..7825577 100644 --- a/components/MDXButton/index.tsx +++ b/components/MDXButton/index.tsx @@ -1,7 +1,7 @@ import { useContext } from 'react'; import { ThemeContext } from '../../styles/theme'; import { Props } from './types'; -import { Btn } from './styles'; +import { StyledLink, StyledButton } from './styles'; const MDXButton = ({ variant = 'text', @@ -15,7 +15,7 @@ const MDXButton = ({ const { mode } = useContext(ThemeContext); return link ? ( - {children} - + ) : ( - + {children} - + ); }; diff --git a/components/MDXButton/styles.tsx b/components/MDXButton/styles.tsx index 29c17c2..2984e5e 100644 --- a/components/MDXButton/styles.tsx +++ b/components/MDXButton/styles.tsx @@ -1,13 +1,12 @@ import Link from 'next/link'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; import { Props } from './types'; -export const Btn = styled(Link)` +export const sharedStyles = css` cursor: pointer; display: ${({ variant }) => ['action', 'outline'].includes(variant as string) ? 'block' : 'inline'}; width: ${({ variant }) => (['action', 'outline'].includes(variant as string) ? '100%' : 'auto')}; - /* TODO: fix theme blue color problem */ background: ${({ variant }) => (variant === 'action' ? '#1573CA' : 'none')}; color: ${({ variant, mode }) => variant === 'action' ? 'white' : mode === 'dark' ? 'white' : 'black'}; @@ -25,9 +24,7 @@ export const Btn = styled(Link)` text-decoration: none; transition: color 250ms ease-in-out; - ${({ disabled }) => - disabled && - ` + ${({ disabled }) => disabled && ` background: gray; cursor: default; `} @@ -37,3 +34,11 @@ export const Btn = styled(Link)` ['action', 'outline'].includes(variant as string) ? '.5rem .75rem' : '0rem'}; } `; + +export const StyledLink = styled(Link)` + ${sharedStyles} +`; + +export const StyledButton = styled.button>` + ${sharedStyles} +`; diff --git a/components/MobileNav/index.tsx b/components/MobileNav/index.tsx index 43df7f5..813a8f96 100644 --- a/components/MobileNav/index.tsx +++ b/components/MobileNav/index.tsx @@ -1,11 +1,11 @@ -import { FC, useContext, useRef, useEffect } from 'react'; +import { useContext, useRef, useEffect } from 'react'; import { ThemeContext } from '../../styles/theme'; import { Props } from './types'; import { Bar } from './styles'; import IconButton from '../IconButton'; import Button from '../Button'; -const MobileNav: FC = ({ open, close }) => { +const MobileNav = ({ open, close }: Props) => { const { mode, toggle } = useContext(ThemeContext); const ref = useRef(null); @@ -45,7 +45,7 @@ const MobileNav: FC = ({ open, close }) => { close(); }} > - {mode === 'dark' ? 'Light Mode' : 'Dark Mode'} + {mode === 'dark' ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
diff --git a/components/Nav/index.tsx b/components/Nav/index.tsx index 52593ed..5f38349 100644 --- a/components/Nav/index.tsx +++ b/components/Nav/index.tsx @@ -1,4 +1,4 @@ -import { FC, useContext, useState } from 'react'; +import { useContext, useState } from 'react'; import { ThemeContext } from '../../styles/theme'; import { Bar } from './styles'; import Link from 'next/link'; @@ -7,7 +7,7 @@ import Button from '../Button'; import IconButton from '../IconButton'; import MobileNav from '../MobileNav'; -const Nav: FC = () => { +const Nav = () => { const [mobileNavOpen, setMobileNavOpen] = useState(false); const { mode, toggle } = useContext(ThemeContext); @@ -41,7 +41,7 @@ const Nav: FC = () => { Resume setMobileNavOpen(true)} /> diff --git a/pages/404.tsx b/pages/404.tsx index 89f7afe..67efefa 100644 --- a/pages/404.tsx +++ b/pages/404.tsx @@ -1,10 +1,9 @@ -import { FC } from 'react'; import { useRouter } from 'next/router'; import Head from 'next/head'; import IconButton from '../components/IconButton'; import { Wrapper } from '../styles/404'; -const NotFound: FC = () => { +const NotFound = () => { const router = useRouter(); return ( diff --git a/pages/blog/[slug].tsx b/pages/blog/[slug].tsx index c50151e..b7676ae 100644 --- a/pages/blog/[slug].tsx +++ b/pages/blog/[slug].tsx @@ -1,4 +1,4 @@ -import { FC, useEffect } from 'react'; +import { useEffect } from 'react'; import { getBlogPostsSlugs, getBlogPostdata } from '../../utils/blog'; import { useRouter } from 'next/router'; import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'; @@ -19,7 +19,7 @@ interface Props { text: string; } -const BlogPost: FC = ({ source, frontMatter, text }) => { +const BlogPost = ({ source, frontMatter, text }: Props) => { const router = useRouter(); const stats = readingTime(text); const htmlOverrides = { code: CodeBlock }; diff --git a/pages/blog/index.tsx b/pages/blog/index.tsx index 82fce7e..9bcc1c5 100644 --- a/pages/blog/index.tsx +++ b/pages/blog/index.tsx @@ -1,4 +1,3 @@ -import React, { FC } from 'react'; import { useRouter } from 'next/router'; import { getBlogPosts } from '../../utils/blog'; import { Wrapper } from '../../styles/blog'; @@ -18,7 +17,7 @@ interface Props { }[]; } -const Index: FC = ({ blogPosts }) => { +const Index = ({ blogPosts }: Props) => { const router = useRouter(); return ( diff --git a/pages/contact.tsx b/pages/contact.tsx index 666aed8..685893e 100644 --- a/pages/contact.tsx +++ b/pages/contact.tsx @@ -1,11 +1,11 @@ -import React, { FC, useState } from 'react'; +import { useState } from 'react'; import { useForm, ValidationError } from '@formspree/react'; import Head from 'next/head'; import { Wrapper } from '../styles/contact'; import Input from '../components/Input'; import MDXButton from '../components/MDXButton'; -const About: FC = () => { +const About = () => { const [form, setForm] = useState<{ name: string; email: string; message: string }>({ name: '', email: '', diff --git a/pages/index.tsx b/pages/index.tsx index 05910ae..1677604 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,4 +1,3 @@ -import { FC } from 'react'; import { getPortfolioProjects } from '../utils/portfolio'; import { getBlogPosts } from '../utils/blog'; import { GetStaticProps } from 'next'; @@ -26,7 +25,7 @@ interface Props { }[]; } -const Index: FC = ({ blogPosts, portfolioProjects }) => { +const Index = ({ blogPosts, portfolioProjects }: Props) => { return ( <> diff --git a/pages/portfolio/[slug].tsx b/pages/portfolio/[slug].tsx index 8dcb432..18e6dc6 100644 --- a/pages/portfolio/[slug].tsx +++ b/pages/portfolio/[slug].tsx @@ -1,4 +1,4 @@ -import { FC, useEffect } from 'react'; +import { useEffect } from 'react'; import { getPortfolioPorjectsSlugs, getPortfolioProjectdata } from '../../utils/portfolio'; import { useRouter } from 'next/router'; import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'; @@ -18,7 +18,7 @@ interface Props { frontMatter: any; } -const PortfolioProject: FC = ({ source, frontMatter }) => { +const PortfolioProject = ({ source, frontMatter }: Props) => { const router = useRouter(); const htmlOverrides = { code: CodeBlock }; const mdxComponents = { Button: MDXButton }; diff --git a/pages/portfolio/index.tsx b/pages/portfolio/index.tsx index 4f37de4..63c7e08 100644 --- a/pages/portfolio/index.tsx +++ b/pages/portfolio/index.tsx @@ -1,4 +1,3 @@ -import React, { FC } from 'react'; import { getPortfolioProjects } from '../../utils/portfolio'; import { useRouter } from 'next/router'; import { Wrapper } from '../../styles/portfolio'; @@ -17,7 +16,7 @@ interface Props { }[]; } -const Index: FC = ({ portfolioProjects }) => { +const Index = ({ portfolioProjects }: Props) => { const router = useRouter(); return (