mirror of
https://github.com/hazemKrimi/personal-website.git
synced 2026-05-01 18:00:26 +00:00
Update button behavior to work as link
This commit is contained in:
@@ -19,10 +19,10 @@ This is a project that I made as a step in the interview process for my final ye
|
||||
|
||||
# Showcase
|
||||
|
||||
<Button variant='action' link='https://hazemkrimi.github.io/react-weather-app'>
|
||||
<Button variant='action' link='https://hazemkrimi.github.io/react-weather-app' target='_blank'>
|
||||
Demo
|
||||
</Button>
|
||||
|
||||
<Button variant='outline' link='https://github.com/hazemKrimi/react-weather-app'>
|
||||
<Button variant='outline' link='https://github.com/hazemKrimi/react-weather-app' target='_blank'>
|
||||
Source Code
|
||||
</Button>
|
||||
|
||||
+20
-2
@@ -1,13 +1,17 @@
|
||||
import { FC, useContext } from 'react';
|
||||
import { DarkModeContext } from '../components/DarkMode';
|
||||
import styled from 'styled-components';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface Props {
|
||||
variant?: 'outline' | 'text';
|
||||
href: string;
|
||||
target?: HTMLAnchorElement['target'];
|
||||
onClick?: () => void;
|
||||
dark?: boolean;
|
||||
}
|
||||
|
||||
const Btn = styled.button<Props>`
|
||||
const Btn = styled.button<Omit<Props, 'href'>>`
|
||||
position: relative;
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
@@ -19,6 +23,7 @@ const Btn = styled.button<Props>`
|
||||
text-transform: ${({ variant }) => (variant === 'outline' ? 'uppercase' : 'inherit')};
|
||||
padding: ${({ variant }) => (variant === 'outline' ? '.5rem 1rem' : '0rem')};
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
transition: color 250ms ease-in-out;
|
||||
z-index: 1;
|
||||
|
||||
@@ -41,8 +46,10 @@ const Btn = styled.button<Props>`
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@media (min-width: 768px) {
|
||||
color: ${({ variant }) => (variant === 'outline' ? 'var(--background)' : 'inherit')};
|
||||
}
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
transform: scaleX(1);
|
||||
@@ -51,6 +58,8 @@ const Btn = styled.button<Props>`
|
||||
|
||||
const Button: FC<Props & { className?: string }> = ({
|
||||
variant = 'text',
|
||||
href,
|
||||
target,
|
||||
onClick,
|
||||
children,
|
||||
className
|
||||
@@ -58,9 +67,18 @@ const Button: FC<Props & { className?: string }> = ({
|
||||
const { dark } = useContext(DarkModeContext);
|
||||
|
||||
return (
|
||||
<Btn variant={variant} dark={dark} onClick={onClick} className={className}>
|
||||
<Link href={href} passHref>
|
||||
<Btn
|
||||
as='a'
|
||||
target={target}
|
||||
variant={variant}
|
||||
dark={dark}
|
||||
onClick={onClick}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</Btn>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+9
-2
@@ -2,12 +2,15 @@ import { FC, useContext } from 'react';
|
||||
import { DarkModeContext } from '../components/DarkMode';
|
||||
import styled from 'styled-components';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
tags?: string[];
|
||||
href: string;
|
||||
target?: HTMLAnchorElement['target'];
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
@@ -18,6 +21,8 @@ const StyledCard = styled.div<{ dark: boolean; image: boolean }>`
|
||||
grid-template-columns: auto 150px;
|
||||
align-items: stretch;
|
||||
transition: color 0ms ease-in-out;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
|
||||
&:hover {
|
||||
& > div {
|
||||
@@ -67,11 +72,12 @@ const StyledCard = styled.div<{ dark: boolean; image: boolean }>`
|
||||
}
|
||||
`;
|
||||
|
||||
const Card: FC<Props> = ({ title, description, image, tags, onClick }) => {
|
||||
const Card: FC<Props> = ({ title, description, image, tags, href, target, onClick }) => {
|
||||
const { dark } = useContext(DarkModeContext);
|
||||
|
||||
return (
|
||||
<StyledCard dark={dark} onClick={onClick} image={!!image}>
|
||||
<Link href={href} passHref>
|
||||
<StyledCard as='a' target={target} dark={dark} onClick={onClick} image={!!image}>
|
||||
<div>
|
||||
<h3>{title}</h3>
|
||||
<p>{description}</p>
|
||||
@@ -89,6 +95,7 @@ const Card: FC<Props> = ({ title, description, image, tags, onClick }) => {
|
||||
<Image src='/no-image.png' width='100%' height='100%' layout='responsive' />
|
||||
)}
|
||||
</StyledCard>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+10
-5
@@ -53,31 +53,36 @@ const Footer: FC = () => {
|
||||
icon={dark ? '/icons/light-github.svg' : '/icons/dark-github.svg'}
|
||||
width={16}
|
||||
height={16}
|
||||
onClick={() => window.open('https://github.com/hazemKrimi', '_blank')}
|
||||
href='https://github.com/hazemKrimi'
|
||||
target='_blank'
|
||||
/>
|
||||
<IconButton
|
||||
icon={dark ? '/icons/light-twitter.svg' : '/icons/dark-twitter.svg'}
|
||||
width={16}
|
||||
height={16}
|
||||
onClick={() => window.open('https://twitter.com/HazemKrimi', '_blank')}
|
||||
href='https://twitter.com/HazemKrimi'
|
||||
target='_blank'
|
||||
/>
|
||||
<IconButton
|
||||
icon={dark ? '/icons/light-linkedin.svg' : '/icons/dark-linkedin.svg'}
|
||||
width={16}
|
||||
height={16}
|
||||
onClick={() => window.open('https://linkedin.com/in/hazemkrimi', '_blank')}
|
||||
href='https://linkedin.com/in/hazemkrimi'
|
||||
target='_blank'
|
||||
/>
|
||||
<IconButton
|
||||
icon={dark ? '/icons/light-codepen.svg' : '/icons/dark-codepen.svg'}
|
||||
width={16}
|
||||
height={16}
|
||||
onClick={() => window.open('https://codepen.io/hazemkrimi', '_blank')}
|
||||
href='https://codepen.io/hazemkrimi'
|
||||
target='_blank'
|
||||
/>
|
||||
<IconButton
|
||||
icon={dark ? '/icons/light-dribbble.svg' : '/icons/dark-dribbble.svg'}
|
||||
width={16}
|
||||
height={16}
|
||||
onClick={() => window.open('https://dribbble.com/HazemKrimi', '_blank')}
|
||||
href='https://dribbble.com/HazemKrimi'
|
||||
target='_blank'
|
||||
/>
|
||||
</div>
|
||||
<p>Hazem Krimi © {new Date().getFullYear()}</p>
|
||||
|
||||
@@ -55,7 +55,6 @@ const Hero: FC = () => {
|
||||
<h2>Hi, I am Hazem</h2>
|
||||
<h2>I Like Building Things</h2>
|
||||
<h2 className='blue'>Software Developer</h2>
|
||||
<h2 className='blue'>Hard Working</h2>
|
||||
<h2 className='blue'>Life Long Learner</h2>
|
||||
</div>
|
||||
<div className='illustration'>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { FC } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface Props {
|
||||
icon: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
href?: string;
|
||||
target?: HTMLAnchorElement['target'];
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
@@ -18,15 +22,23 @@ const Btn = styled.button`
|
||||
|
||||
const IconButton: FC<Props & { className?: string }> = ({
|
||||
icon,
|
||||
href,
|
||||
target,
|
||||
onClick,
|
||||
className,
|
||||
width = 24,
|
||||
height = 24
|
||||
}) => {
|
||||
return (
|
||||
return !href ? (
|
||||
<Btn onClick={onClick} className={className}>
|
||||
<Image src={icon} width={width} height={height} />
|
||||
</Btn>
|
||||
) : (
|
||||
<Link href={href} passHref>
|
||||
<Btn as='a' target={target} onClick={onClick} className={className}>
|
||||
<Image src={icon} width={width} height={height} />
|
||||
</Btn>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { FC, useContext } from 'react';
|
||||
import { DarkModeContext } from '../components/DarkMode';
|
||||
import Link from 'next/link';
|
||||
import styled from 'styled-components';
|
||||
|
||||
interface Props {
|
||||
variant?: 'outline' | 'text' | 'action';
|
||||
type?: 'button' | 'submit';
|
||||
link?: string;
|
||||
target?: HTMLAnchorElement['target'];
|
||||
dark?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
@@ -29,6 +31,7 @@ const Btn = styled.button<Props>`
|
||||
['action', 'outline'].includes(variant as string) ? '.5rem 1rem' : '0rem'};
|
||||
text-align: ${({ variant }) =>
|
||||
['action', 'outline'].includes(variant as string) ? 'center' : 'left'};
|
||||
text-decoration: none;
|
||||
transition: color 250ms ease-in-out;
|
||||
|
||||
${({ disabled }) =>
|
||||
@@ -48,25 +51,31 @@ const MDXButton: FC<Props & { className?: string }> = ({
|
||||
variant = 'text',
|
||||
type = 'button',
|
||||
link,
|
||||
target,
|
||||
children,
|
||||
disabled,
|
||||
className
|
||||
}) => {
|
||||
const { dark } = useContext(DarkModeContext);
|
||||
|
||||
return (
|
||||
return link ? (
|
||||
<Link href={link} passHref>
|
||||
<Btn
|
||||
as='a'
|
||||
target={target}
|
||||
variant={variant}
|
||||
type={type}
|
||||
dark={dark}
|
||||
onClick={() => {
|
||||
if (link) window.open(link, '_blank');
|
||||
}}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</Btn>
|
||||
</Link>
|
||||
) : (
|
||||
<Btn variant={variant} type={type} dark={dark} disabled={disabled} className={className}>
|
||||
{children}
|
||||
</Btn>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { FC, useContext, useRef, useEffect } from 'react';
|
||||
import { DarkModeContext } from './DarkMode';
|
||||
import { useRouter } from 'next/router';
|
||||
import styled from 'styled-components';
|
||||
import IconButton from './IconButton';
|
||||
import Button from './Button';
|
||||
@@ -44,7 +43,7 @@ const Bar = styled.nav<StyledProps>`
|
||||
display: flex;
|
||||
margin: 0rem 1rem;
|
||||
|
||||
button {
|
||||
a {
|
||||
color: var(--text-inverted) !important;
|
||||
}
|
||||
}
|
||||
@@ -53,7 +52,6 @@ const Bar = styled.nav<StyledProps>`
|
||||
const MobileNav: FC<Props> = ({ open, close }) => {
|
||||
const { dark, toggle } = useContext(DarkModeContext);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousedown', (event: MouseEvent) => {
|
||||
@@ -83,37 +81,18 @@ const MobileNav: FC<Props> = ({ open, close }) => {
|
||||
/>
|
||||
</div>
|
||||
<div className='mobile-button-wrapper'>
|
||||
<Button onClick={() => toggle()}>{dark ? 'Light Mode' : 'Dark Mode'}</Button>
|
||||
</div>
|
||||
<div className='mobile-button-wrapper'>
|
||||
<Button
|
||||
onClick={() => {
|
||||
close();
|
||||
router.push('/about');
|
||||
}}
|
||||
>
|
||||
About
|
||||
<Button href='#' onClick={() => toggle()}>
|
||||
{dark ? 'Light Mode' : 'Dark Mode'}
|
||||
</Button>
|
||||
</div>
|
||||
<div className='mobile-button-wrapper'>
|
||||
<Button
|
||||
onClick={() => {
|
||||
close();
|
||||
router.push('/portfolio');
|
||||
}}
|
||||
>
|
||||
Portfolio
|
||||
</Button>
|
||||
<Button href='/about'>About</Button>
|
||||
</div>
|
||||
<div className='mobile-button-wrapper'>
|
||||
<Button
|
||||
onClick={() => {
|
||||
close();
|
||||
router.push('/blog');
|
||||
}}
|
||||
>
|
||||
Blog
|
||||
</Button>
|
||||
<Button href='/portfolio'>Portfolio</Button>
|
||||
</div>
|
||||
<div className='mobile-button-wrapper'>
|
||||
<Button href='/blog'>Blog</Button>
|
||||
</div>
|
||||
</Bar>
|
||||
);
|
||||
|
||||
+15
-11
@@ -1,7 +1,7 @@
|
||||
import { FC, useContext, useState } from 'react';
|
||||
import { DarkModeContext } from './DarkMode';
|
||||
import { useRouter } from 'next/router';
|
||||
import styled from 'styled-components';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import Button from './Button';
|
||||
import IconButton from './IconButton';
|
||||
@@ -26,7 +26,8 @@ const Bar = styled.nav`
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
div,
|
||||
a.logo {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
column-gap: 1rem;
|
||||
@@ -36,7 +37,9 @@ const Bar = styled.nav`
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
a.logo {
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
grid-template-columns: repeat(2, auto);
|
||||
justify-content: flex-start;
|
||||
@@ -65,11 +68,11 @@ const Bar = styled.nav`
|
||||
const Nav: FC = () => {
|
||||
const [mobileNavOpen, setMobileNavOpen] = useState<boolean>(false);
|
||||
const { dark, toggle } = useContext(DarkModeContext);
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<Bar>
|
||||
<div className='logo' onClick={() => router.push('/')}>
|
||||
<Link href='/' passHref>
|
||||
<a className='logo'>
|
||||
<Image
|
||||
className='logo-image'
|
||||
src={dark ? '/light-logo.svg' : '/dark-logo.svg'}
|
||||
@@ -78,18 +81,19 @@ const Nav: FC = () => {
|
||||
height={48}
|
||||
/>
|
||||
<h1>Hazem Krimi</h1>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className='buttons'>
|
||||
<IconButton icon={dark ? '/icons/sun.svg' : '/icons/moon.svg'} onClick={toggle} />
|
||||
<Button onClick={() => router.push('/about')}>About</Button>
|
||||
<Button onClick={() => router.push('/portfolio')}>Portfolio</Button>
|
||||
<Button onClick={() => router.push('/blog')}>Blog</Button>
|
||||
<Button variant='outline' onClick={() => window.open('/resume.pdf', '_blank')}>
|
||||
<Button href='/about'>About</Button>
|
||||
<Button href='/portfolio'>Portfolio</Button>
|
||||
<Button href='/blog'>Blog</Button>
|
||||
<Button href='/resume.pdf' target='_blank' variant='outline'>
|
||||
Resume
|
||||
</Button>
|
||||
</div>
|
||||
<div className='mobile-buttons'>
|
||||
<Button variant='outline' onClick={() => window.open('/hazem-krimi.pdf', '_blank')}>
|
||||
<Button href='/resume.pdf' target='_blank' variant='outline'>
|
||||
Resume
|
||||
</Button>
|
||||
<IconButton
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"@formspree/react": "^2.2.3",
|
||||
"@mdx-js/loader": "^1.6.22",
|
||||
"@next/mdx": "^10.0.4",
|
||||
"framer-motion": "^4.1.17",
|
||||
"gray-matter": "^4.0.2",
|
||||
"mdx-embed": "^0.0.17",
|
||||
"next": "^11.1.0",
|
||||
|
||||
@@ -86,7 +86,7 @@ const Index: FC<Props> = ({ blogPosts }) => {
|
||||
<div className='articles-wrapper'>
|
||||
{blogPosts.length !== 0 ? (
|
||||
blogPosts.map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} onClick={() => router.push(`/blog/${slug}`)} />
|
||||
<Card {...rest} key={slug} href={`/blog/${slug}`} />
|
||||
))
|
||||
) : (
|
||||
<h4>Nothing for now</h4>
|
||||
|
||||
+4
-9
@@ -1,5 +1,4 @@
|
||||
import { FC } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getPortfolioProjects } from '../utils/portfolio';
|
||||
import { getBlogPosts } from '../utils/blog';
|
||||
import { GetStaticProps } from 'next';
|
||||
@@ -75,8 +74,6 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
const Index: FC<Props> = ({ blogPosts, portfolioProjects }) => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@@ -103,7 +100,7 @@ const Index: FC<Props> = ({ blogPosts, portfolioProjects }) => {
|
||||
<Hero />
|
||||
<div className='content'>
|
||||
<h1>Portfolio</h1>
|
||||
<Button className='blue' onClick={() => router.push('/portfolio')}>
|
||||
<Button href='/portfolio' className='blue'>
|
||||
See More
|
||||
</Button>
|
||||
<div className='portfolio'>
|
||||
@@ -112,7 +109,7 @@ const Index: FC<Props> = ({ blogPosts, portfolioProjects }) => {
|
||||
portfolioProjects
|
||||
.slice(0, 3)
|
||||
.map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} onClick={() => router.push(`/portfolio/${slug}`)} />
|
||||
<Card {...rest} key={slug} href={`/portfolio/${slug}`} />
|
||||
))
|
||||
) : (
|
||||
<h4>Nothing for now</h4>
|
||||
@@ -120,7 +117,7 @@ const Index: FC<Props> = ({ blogPosts, portfolioProjects }) => {
|
||||
</div>
|
||||
</div>
|
||||
<h1>Blog</h1>
|
||||
<Button className='blue' onClick={() => router.push('/blog')}>
|
||||
<Button href='/blog' className='blue'>
|
||||
See More
|
||||
</Button>
|
||||
<div className='blog'>
|
||||
@@ -128,9 +125,7 @@ const Index: FC<Props> = ({ blogPosts, portfolioProjects }) => {
|
||||
{blogPosts.length !== 0 ? (
|
||||
blogPosts
|
||||
.slice(0, 3)
|
||||
.map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} onClick={() => router.push(`/blog/${slug}`)} />
|
||||
))
|
||||
.map(({ slug, ...rest }) => <Card {...rest} key={slug} href={`/blog/${slug}`} />)
|
||||
) : (
|
||||
<h4>Nothing for now</h4>
|
||||
)}
|
||||
|
||||
@@ -85,7 +85,7 @@ const Index: FC<Props> = ({ portfolioProjects }) => {
|
||||
<div className='projects-wrapper'>
|
||||
{portfolioProjects.length !== 0 ? (
|
||||
portfolioProjects.map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} onClick={() => router.push(`/portfolio/${slug}`)} />
|
||||
<Card {...rest} key={slug} href={`/portfolio/${slug}`} />
|
||||
))
|
||||
) : (
|
||||
<h4>Nothing for now</h4>
|
||||
|
||||
Reference in New Issue
Block a user