Add prettier configuration

This commit is contained in:
Hazem Krimi
2023-07-01 00:38:49 +01:00
parent de22cafb12
commit 5e01140d6e
57 changed files with 1896 additions and 1718 deletions
+15 -9
View File
@@ -2,16 +2,22 @@ import { Props } from './types';
import { StyledButton } from './styles';
const Button = ({
variant = 'text',
href,
target,
onClick,
children,
className
variant = 'text',
href,
target,
onClick,
children,
className,
}: Props) => (
<StyledButton href={href} target={target} className={className} onClick={onClick} variant={variant}>
{children}
</StyledButton>
<StyledButton
href={href}
target={target}
className={className}
onClick={onClick}
variant={variant}
>
{children}
</StyledButton>
);
export default Button;
+41 -37
View File
@@ -3,45 +3,49 @@ import Link from 'next/link';
import { Props } from './types';
export const StyledButton = styled(Link)<Props>`
position: relative;
display: inline;
cursor: pointer;
background: none;
color: var(--text);
border: ${({ variant }) =>
variant === 'outline' ? '2px solid var(--text)' : '2px solid transparent'};
font-weight: bold;
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, border 250ms ease-in-out;
z-index: 1;
position: relative;
display: inline;
cursor: pointer;
background: none;
color: var(--text);
border: ${({ variant }) =>
variant === 'outline' ? '2px solid var(--text)' : '2px solid transparent'};
font-weight: bold;
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, border 250ms ease-in-out;
z-index: 1;
@media (max-width: 768px) {
padding: ${({ variant }) => (variant === 'outline' ? '.5rem .75rem' : '0rem')};
}
@media (max-width: 768px) {
padding: ${({ variant }) =>
variant === 'outline' ? '.5rem .75rem' : '0rem'};
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: -1;
background-color: ${({ variant }) => (variant === 'outline' ? 'var(--text)' : 'inherit')};
transition: transform 250ms ease-in-out;
transform: scaleX(0);
transform-origin: left;
}
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: -1;
background-color: ${({ variant }) =>
variant === 'outline' ? 'var(--text)' : 'inherit'};
transition: transform 250ms ease-in-out;
transform: scaleX(0);
transform-origin: left;
}
&:hover {
color: ${({ variant }) => (variant === 'outline' ? 'var(--background)' : 'inherit')};
border: 2px solid transparent;
}
&:hover {
color: ${({ variant }) =>
variant === 'outline' ? 'var(--background)' : 'inherit'};
border: 2px solid transparent;
}
&:hover::before {
transform: scaleX(1);
}
&:hover::before {
transform: scaleX(1);
}
`;
+6 -6
View File
@@ -1,8 +1,8 @@
export type Props = {
variant?: 'outline' | 'text';
href: string;
target?: HTMLAnchorElement['target'];
onClick?: () => void;
children: React.ReactNode;
className?: string;
variant?: 'outline' | 'text';
href: string;
target?: HTMLAnchorElement['target'];
onClick?: () => void;
children: React.ReactNode;
className?: string;
};
+34 -21
View File
@@ -2,27 +2,40 @@ import Image from 'next/image';
import { StyledCard } from './styles';
import { Props } from './types';
const Card = ({ title, description, image, tags, href, target, onClick }: Props) => {
return (
<StyledCard href={href} onClick={onClick} image={image ? Boolean(image) : undefined} target={target}>
<div className='card-content'>
<h3>{title}</h3>
<p>{description}</p>
{tags && (
<div className='tags-wrapper'>
{tags.map((tag, index) => (
<span key={index}>#{tag}&nbsp;</span>
))}
</div>
)}
</div>
{image && (
<div className='card-image'>
<Image alt={title} src={image} fill />
</div>
)}
</StyledCard>
);
const Card = ({
title,
description,
image,
tags,
href,
target,
onClick,
}: Props) => {
return (
<StyledCard
href={href}
onClick={onClick}
image={image ? Boolean(image) : undefined}
target={target}
>
<div className='card-content'>
<h3>{title}</h3>
<p>{description}</p>
{tags && (
<div className='tags-wrapper'>
{tags.map((tag, index) => (
<span key={index}>#{tag}&nbsp;</span>
))}
</div>
)}
</div>
{image && (
<div className='card-image'>
<Image alt={title} src={image} fill />
</div>
)}
</StyledCard>
);
};
export default Card;
+59 -58
View File
@@ -2,75 +2,76 @@ import styled from 'styled-components';
import Link from 'next/link';
export const StyledCard = styled(Link)<{ image?: boolean }>`
cursor: pointer;
width: 100%;
display: grid;
grid-template-columns: ${({ image }) => (image ? 'auto 9.375rem' : 'auto')};
align-items: stretch;
transition: color 0ms ease-in-out;
text-decoration: none;
color: var(--text);
cursor: pointer;
width: 100%;
display: grid;
grid-template-columns: ${({ image }) => (image ? 'auto 9.375rem' : 'auto')};
align-items: stretch;
transition: color 0ms ease-in-out;
text-decoration: none;
color: var(--text);
@media (max-width: 320px) {
grid-template-columns: ${({ image }) => (image ? 'auto 7.813rem' : 'auto')};
}
@media (max-width: 320px) {
grid-template-columns: ${({ image }) => (image ? 'auto 7.813rem' : 'auto')};
}
@media (min-width: 1440px) {
grid-template-columns: ${({ image }) => (image ? 'auto 15.625rem' : 'auto')};
}
@media (min-width: 1440px) {
grid-template-columns: ${({ image }) =>
image ? 'auto 15.625rem' : 'auto'};
}
&:hover {
& > div {
background: ${({ theme }) => theme.colors.blue};
&:hover {
& > div {
background: ${({ theme }) => theme.colors.blue};
* {
color: ${({ theme }) => theme.colors.dark.text} !important;
}
}
* {
color: ${({ theme }) => theme.colors.dark.text} !important;
}
}
img {
filter: ${({ image }) => (image ? 'grayscale(80%)' : 'none')};
}
}
img {
filter: ${({ image }) => (image ? 'grayscale(80%)' : 'none')};
}
}
.card-content {
padding: 1rem 0rem;
background: var(--secondary-background);
display: grid;
row-gap: 0.5rem;
.card-content {
padding: 1rem 0rem;
background: var(--secondary-background);
display: grid;
row-gap: 0.5rem;
@media (max-width: 768px) {
padding: 0.75rem 0rem;
}
}
@media (max-width: 768px) {
padding: 0.75rem 0rem;
}
}
.card-image {
position: relative;
width: 100%;
}
.card-image {
position: relative;
width: 100%;
}
h3,
p,
.tags-wrapper {
padding: 0rem 1rem;
h3,
p,
.tags-wrapper {
padding: 0rem 1rem;
@media (max-width: 768px) {
padding: 0rem 0.5rem;
}
}
@media (max-width: 768px) {
padding: 0rem 0.5rem;
}
}
h3 {
font-size: 1.3rem;
}
h3 {
font-size: 1.3rem;
}
.tags-wrapper {
display: flex;
flex-direction: row;
align-content: center;
flex-wrap: wrap;
}
.tags-wrapper {
display: flex;
flex-direction: row;
align-content: center;
flex-wrap: wrap;
}
span {
font-size: 0.7rem;
}
span {
font-size: 0.7rem;
}
`;
+8 -8
View File
@@ -1,9 +1,9 @@
export interface Props {
title: string;
description: string;
image?: string;
tags?: string[];
href: string;
target?: HTMLAnchorElement['target'];
onClick?: () => void;
}
title: string;
description: string;
image?: string;
tags?: string[];
href: string;
target?: HTMLAnchorElement['target'];
onClick?: () => void;
}
+24 -24
View File
@@ -4,31 +4,31 @@ import { Props } from './types';
import { Line, LineContent, LineNo, Pre } from './styles';
const CodeBlock = ({ children, className }: Props) => {
const language = className.replace(/language-/, '') as Language;
const language = className.replace(/language-/, '') as Language;
return (
<Highlight
{...defaultProps}
theme={theme}
code={(children as string).trim()}
language={language}
>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<Pre className={className} style={style}>
{tokens.map((line, i) => (
<Line key={i} {...getLineProps({ line, key: i })}>
<LineNo>{i + 1}</LineNo>
<LineContent>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</LineContent>
</Line>
))}
</Pre>
)}
</Highlight>
);
return (
<Highlight
{...defaultProps}
theme={theme}
code={(children as string).trim()}
language={language}
>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<Pre className={className} style={style}>
{tokens.map((line, i) => (
<Line key={i} {...getLineProps({ line, key: i })}>
<LineNo>{i + 1}</LineNo>
<LineContent>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</LineContent>
</Line>
))}
</Pre>
)}
</Highlight>
);
};
export default CodeBlock;
+11 -11
View File
@@ -1,24 +1,24 @@
import styled from 'styled-components';
export const Pre = styled.pre`
text-align: left;
margin: 1em 0;
padding: 0.5em;
overflow: scroll;
text-align: left;
margin: 1em 0;
padding: 0.5em;
overflow: scroll;
`;
export const Line = styled.div`
display: table-row;
display: table-row;
`;
export const LineNo = styled.span`
display: table-cell;
text-align: right;
padding-right: 1em;
user-select: none;
opacity: 0.5;
display: table-cell;
text-align: right;
padding-right: 1em;
user-select: none;
opacity: 0.5;
`;
export const LineContent = styled.span`
display: table-cell;
display: table-cell;
`;
+3 -3
View File
@@ -1,4 +1,4 @@
export interface Props {
className: string;
children: React.ReactNode;
}
className: string;
children: React.ReactNode;
}
+5 -5
View File
@@ -1,12 +1,12 @@
import styled from 'styled-components';
const Container = styled.div`
width: 85%;
margin: auto;
width: 85%;
margin: auto;
@media (max-width: 768px) {
width: 95%;
}
@media (max-width: 768px) {
width: 95%;
}
`;
export default Container;
+44 -32
View File
@@ -4,39 +4,51 @@ import { StyledFooter } from './styles';
import IconButton from '../IconButton';
const Footer = () => {
const { mode } = useContext(ThemeContext);
const { mode } = useContext(ThemeContext);
return (
<StyledFooter>
<div className='contact'>
<IconButton
alt='GitHub'
icon={mode === 'dark' ? '/icons/light-github.svg' : '/icons/dark-github.svg'}
width={16}
height={16}
href='https://github.com/hazemKrimi'
target='_blank'
/>
<IconButton
alt='Twitter'
icon={mode === 'dark' ? '/icons/light-twitter.svg' : '/icons/dark-twitter.svg'}
width={16}
height={16}
href='https://twitter.com/HazemKrimi'
target='_blank'
/>
<IconButton
alt='LinkedIn'
icon={mode === 'dark' ? '/icons/light-linkedin.svg' : '/icons/dark-linkedin.svg'}
width={16}
height={16}
href='https://linkedin.com/in/hazemkrimi'
target='_blank'
/>
</div>
<p>Hazem Krimi &copy; {new Date().getFullYear()}</p>
</StyledFooter>
);
return (
<StyledFooter>
<div className='contact'>
<IconButton
alt='GitHub'
icon={
mode === 'dark'
? '/icons/light-github.svg'
: '/icons/dark-github.svg'
}
width={16}
height={16}
href='https://github.com/hazemKrimi'
target='_blank'
/>
<IconButton
alt='Twitter'
icon={
mode === 'dark'
? '/icons/light-twitter.svg'
: '/icons/dark-twitter.svg'
}
width={16}
height={16}
href='https://twitter.com/HazemKrimi'
target='_blank'
/>
<IconButton
alt='LinkedIn'
icon={
mode === 'dark'
? '/icons/light-linkedin.svg'
: '/icons/dark-linkedin.svg'
}
width={16}
height={16}
href='https://linkedin.com/in/hazemkrimi'
target='_blank'
/>
</div>
<p>Hazem Krimi &copy; {new Date().getFullYear()}</p>
</StyledFooter>
);
};
export default Footer;
+32 -32
View File
@@ -1,41 +1,41 @@
import styled from 'styled-components';
export const StyledFooter = styled.footer`
position: absolute;
bottom: 0;
min-height: 100px;
width: 85%;
margin: auto;
display: grid;
grid-template-columns: repeat(2, 1fr);
column-gap: 2rem;
justify-content: flex-end;
align-content: center;
padding: 1rem 0rem;
position: absolute;
bottom: 0;
min-height: 100px;
width: 85%;
margin: auto;
display: grid;
grid-template-columns: repeat(2, 1fr);
column-gap: 2rem;
justify-content: flex-end;
align-content: center;
padding: 1rem 0rem;
@media (max-width: 768px) {
width: 95%;
}
@media (max-width: 768px) {
width: 95%;
}
.contact {
display: grid;
grid-template-columns: repeat(auto-fill, 16px);
column-gap: 1rem;
align-items: center;
justify-content: flex-start;
.contact {
display: grid;
grid-template-columns: repeat(auto-fill, 16px);
column-gap: 1rem;
align-items: center;
justify-content: flex-start;
* {
user-select: none;
}
* {
user-select: none;
}
@media (max-width: 768px) {
column-gap: 0.5rem;
}
}
@media (max-width: 768px) {
column-gap: 0.5rem;
}
}
p {
display: inline;
text-align: right;
font-weight: bold;
}
p {
display: inline;
text-align: right;
font-weight: bold;
}
`;
+11 -11
View File
@@ -2,17 +2,17 @@ import { Wrapper } from './styles';
import Image from 'next/image';
const Hero = () => (
<Wrapper>
<div className='intro'>
<h2>Hi, I am Hazem</h2>
<h2>I Like Building Software</h2>
<h2 className='blue'>Full Stack TypeScript Developer</h2>
<h2 className='blue'>Life Long Learner</h2>
</div>
<div className='photo'>
<Image alt='Hazem Krimi' src='/photo.jpg' width={515} height={535} />
</div>
</Wrapper>
<Wrapper>
<div className='intro'>
<h2>Hi, I am Hazem</h2>
<h2>I Like Building Software</h2>
<h2 className='blue'>Full Stack TypeScript Developer</h2>
<h2 className='blue'>Life Long Learner</h2>
</div>
<div className='photo'>
<Image alt='Hazem Krimi' src='/photo.jpg' width={515} height={535} />
</div>
</Wrapper>
);
export default Hero;
+25 -25
View File
@@ -1,35 +1,35 @@
import styled from 'styled-components';
export const Wrapper = styled.div`
min-height: 45vh;
display: grid;
grid-template-columns: 1fr 32.188rem;
align-items: center;
height: auto;
text-align: left;
min-height: 45vh;
display: grid;
grid-template-columns: 1fr 32.188rem;
align-items: center;
height: auto;
text-align: left;
@media (max-width: 1024px) {
min-height: 35vh;
grid-template-columns: 1fr;
@media (max-width: 1024px) {
min-height: 35vh;
grid-template-columns: 1fr;
.photo {
display: none;
}
}
.photo {
display: none;
}
}
h2 {
font-size: 1.5rem;
h2 {
font-size: 1.5rem;
@media (min-width: 1440px) {
font-size: 2rem;
}
@media (min-width: 1440px) {
font-size: 2rem;
}
@media (min-width: 2560px) {
font-size: 3.5rem;
}
}
@media (min-width: 2560px) {
font-size: 3.5rem;
}
}
.blue {
color: ${({ theme }) => theme.colors.blue};
}
.blue {
color: ${({ theme }) => theme.colors.blue};
}
`;
+23 -17
View File
@@ -3,22 +3,28 @@ import { Props } from './types';
import { StyledButton, StyledLink } from './styles';
const IconButton = ({
alt,
icon,
href,
target,
onClick,
className,
width = 24,
height = 24
}: Props) => href ? (
<StyledLink href={href} target={target} onClick={onClick} className={className}>
<Image alt={alt} src={icon} width={width} height={height} />
</StyledLink>
) : (
<StyledButton onClick={onClick} className={className}>
<Image alt={alt} src={icon} width={width} height={height} />
</StyledButton>
);
alt,
icon,
href,
target,
onClick,
className,
width = 24,
height = 24,
}: Props) =>
href ? (
<StyledLink
href={href}
target={target}
onClick={onClick}
className={className}
>
<Image alt={alt} src={icon} width={width} height={height} />
</StyledLink>
) : (
<StyledButton onClick={onClick} className={className}>
<Image alt={alt} src={icon} width={width} height={height} />
</StyledButton>
);
export default IconButton;
+8 -8
View File
@@ -2,17 +2,17 @@ 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;
cursor: pointer;
background: none;
border: none;
display: inline-flex;
align-items: center;
`;
export const StyledLink = styled(Link)`
${sharedStyles}
${sharedStyles}
`;
export const StyledButton = styled.button`
${sharedStyles}
`;
${sharedStyles}
`;
+9 -9
View File
@@ -1,10 +1,10 @@
export interface Props {
alt: string;
icon: string;
width?: number;
height?: number;
href?: string;
target?: HTMLAnchorElement['target'];
onClick?: () => void;
className?: string;
}
alt: string;
icon: string;
width?: number;
height?: number;
href?: string;
target?: HTMLAnchorElement['target'];
onClick?: () => void;
className?: string;
}
+31 -31
View File
@@ -1,37 +1,37 @@
import { BigField, SmallField } from "./styles";
import { Props } from "./types";
import { BigField, SmallField } from './styles';
import { Props } from './types';
const Input = ({
type = 'text',
variant = 'small',
name,
value,
required,
placeholder,
className,
onChange
type = 'text',
variant = 'small',
name,
value,
required,
placeholder,
className,
onChange,
}: Props) => {
return variant === 'small' ? (
<SmallField
type={type}
name={name}
value={value}
required={required}
placeholder={placeholder}
className={className}
onChange={onChange}
/>
) : (
<BigField
name={name}
value={value}
required={required}
placeholder={placeholder}
className={className}
onChange={onChange}
rows={3}
/>
);
return variant === 'small' ? (
<SmallField
type={type}
name={name}
value={value}
required={required}
placeholder={placeholder}
className={className}
onChange={onChange}
/>
) : (
<BigField
name={name}
value={value}
required={required}
placeholder={placeholder}
className={className}
onChange={onChange}
rows={3}
/>
);
};
export default Input;
+11 -11
View File
@@ -1,16 +1,16 @@
import styled from "styled-components";
import styled from 'styled-components';
export const SmallField = styled.input`
border: none;
padding: 1rem;
background: var(--secondary-background);
color: var(--text);
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);
`;
resize: none;
border: none;
padding: 1rem;
background: var(--secondary-background);
color: var(--text);
`;
+10 -8
View File
@@ -1,10 +1,12 @@
export interface Props {
placeholder?: string;
type: 'text' | 'email';
variant: 'small' | 'big';
name: string;
value: string;
required?: boolean;
onChange?: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
className?: string;
placeholder?: string;
type: 'text' | 'email';
variant: 'small' | 'big';
name: string;
value: string;
required?: boolean;
onChange?: (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => void;
className?: string;
}
+31 -25
View File
@@ -4,33 +4,39 @@ import { Props } from './types';
import { StyledLink, StyledButton } from './styles';
const MDXButton = ({
variant = 'text',
type = 'button',
link,
target,
children,
disabled,
className
variant = 'text',
type = 'button',
link,
target,
children,
disabled,
className,
}: Props) => {
const { mode } = useContext(ThemeContext);
const { mode } = useContext(ThemeContext);
return link ? (
<StyledLink
href={link}
target={target}
variant={variant}
type={type}
mode={mode}
disabled={disabled}
className={className}
>
{children}
</StyledLink>
) : (
<StyledButton variant={variant} type={type} mode={mode} disabled={disabled} className={className}>
{children}
</StyledButton>
);
return link ? (
<StyledLink
href={link}
target={target}
variant={variant}
type={type}
mode={mode}
disabled={disabled}
className={className}
>
{children}
</StyledLink>
) : (
<StyledButton
variant={variant}
type={type}
mode={mode}
disabled={disabled}
className={className}
>
{children}
</StyledButton>
);
};
export default MDXButton;
+36 -27
View File
@@ -3,42 +3,51 @@ import styled, { css } from 'styled-components';
import { Props } from './types';
export const sharedStyles = css<Props>`
cursor: pointer;
display: ${({ variant }) =>
['action', 'outline'].includes(variant as string) ? 'block' : 'inline'};
width: ${({ variant }) => (['action', 'outline'].includes(variant as string) ? '100%' : 'auto')};
background: ${({ variant }) => (variant === 'action' ? '#1573CA' : 'none')};
color: ${({ variant, mode }) =>
variant === 'action' ? 'white' : mode === 'dark' ? 'white' : 'black'};
border: ${({ variant, mode }) =>
variant === 'outline' ? `2px solid ${mode === 'dark' ? 'white' : 'black'}` : 'none'};
font-weight: bold;
font-size: ${({ variant }) =>
['action', 'outline'].includes(variant as string) ? '1.05rem' : 'inherit'};
text-transform: ${({ variant }) =>
['action', 'outline'].includes(variant as string) ? 'uppercase' : 'inherit'};
padding: ${({ variant }) =>
['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;
cursor: pointer;
display: ${({ variant }) =>
['action', 'outline'].includes(variant as string) ? 'block' : 'inline'};
width: ${({ variant }) =>
['action', 'outline'].includes(variant as string) ? '100%' : 'auto'};
background: ${({ variant }) => (variant === 'action' ? '#1573CA' : 'none')};
color: ${({ variant, mode }) =>
variant === 'action' ? 'white' : mode === 'dark' ? 'white' : 'black'};
border: ${({ variant, mode }) =>
variant === 'outline'
? `2px solid ${mode === 'dark' ? 'white' : 'black'}`
: 'none'};
font-weight: bold;
font-size: ${({ variant }) =>
['action', 'outline'].includes(variant as string) ? '1.05rem' : 'inherit'};
text-transform: ${({ variant }) =>
['action', 'outline'].includes(variant as string)
? 'uppercase'
: 'inherit'};
padding: ${({ variant }) =>
['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 }) => disabled && `
${({ disabled }) =>
disabled &&
`
background: gray;
cursor: default;
`}
@media (max-width: 768px) {
padding: ${({ variant }) =>
['action', 'outline'].includes(variant as string) ? '.5rem .75rem' : '0rem'};
}
@media (max-width: 768px) {
padding: ${({ variant }) =>
['action', 'outline'].includes(variant as string)
? '.5rem .75rem'
: '0rem'};
}
`;
export const StyledLink = styled(Link)<Props>`
${sharedStyles}
${sharedStyles}
`;
export const StyledButton = styled.button<Omit<Props, 'href'>>`
${sharedStyles}
${sharedStyles}
`;
+8 -8
View File
@@ -1,10 +1,10 @@
export type Props = {
variant?: 'outline' | 'text' | 'action';
type?: 'button' | 'submit';
link?: string;
target?: HTMLAnchorElement['target'];
mode?: string;
disabled?: boolean;
className?: string;
children: React.ReactNode;
variant?: 'outline' | 'text' | 'action';
type?: 'button' | 'submit';
link?: string;
target?: HTMLAnchorElement['target'];
mode?: string;
disabled?: boolean;
className?: string;
children: React.ReactNode;
};
+60 -56
View File
@@ -6,65 +6,69 @@ import IconButton from '../IconButton';
import Button from '../Button';
const MobileNav = ({ open, close }: Props) => {
const { mode, toggle } = useContext(ThemeContext);
const ref = useRef<HTMLDivElement>(null);
const { mode, toggle } = useContext(ThemeContext);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
document.addEventListener('mousedown', (event: MouseEvent) => {
if (ref.current && ref.current.contains(event.target as Node)) {
document.addEventListener('mouseup', event => {
if (ref.current && !ref.current.contains(event.target as Node)) return;
});
} else {
document.addEventListener('mouseup', event => {
if (ref.current && !ref.current.contains(event.target as Node)) close();
});
}
});
useEffect(() => {
document.addEventListener('mousedown', (event: MouseEvent) => {
if (ref.current && ref.current.contains(event.target as Node)) {
document.addEventListener('mouseup', (event) => {
if (ref.current && !ref.current.contains(event.target as Node))
return;
});
} else {
document.addEventListener('mouseup', (event) => {
if (ref.current && !ref.current.contains(event.target as Node))
close();
});
}
});
return () => {
document.removeEventListener('mousedown', () => {});
document.removeEventListener('mouseup', () => {});
};
});
return () => {
document.removeEventListener('mousedown', () => {});
document.removeEventListener('mouseup', () => {});
};
});
return (
<Bar open={open} ref={ref}>
<div className='close'>
<IconButton
alt='Theme toggler'
icon={mode === 'dark' ? '/icons/dark-close.svg' : '/icons/light-close.svg'}
onClick={close}
/>
</div>
<div className='mobile-button-wrapper'>
<Button
href='#'
onClick={() => {
toggle();
close();
}}
>
{mode === 'dark' ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
</Button>
</div>
<div className='mobile-button-wrapper'>
<Button href='/projects' onClick={() => close()}>
Projects
</Button>
</div>
<div className='mobile-button-wrapper'>
<Button href='/blog' onClick={() => close()}>
Blog
</Button>
</div>
<div className='mobile-button-wrapper'>
<Button href='/contact' onClick={() => close()}>
Contact
</Button>
</div>
</Bar>
);
return (
<Bar open={open} ref={ref}>
<div className='close'>
<IconButton
alt='Theme toggler'
icon={
mode === 'dark' ? '/icons/dark-close.svg' : '/icons/light-close.svg'
}
onClick={close}
/>
</div>
<div className='mobile-button-wrapper'>
<Button
href='#'
onClick={() => {
toggle();
close();
}}
>
{mode === 'dark' ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
</Button>
</div>
<div className='mobile-button-wrapper'>
<Button href='/projects' onClick={() => close()}>
Projects
</Button>
</div>
<div className='mobile-button-wrapper'>
<Button href='/blog' onClick={() => close()}>
Blog
</Button>
</div>
<div className='mobile-button-wrapper'>
<Button href='/contact' onClick={() => close()}>
Contact
</Button>
</div>
</Bar>
);
};
export default MobileNav;
+28 -28
View File
@@ -2,36 +2,36 @@ import styled from 'styled-components';
import { StyledProps } from './types';
export const Bar = styled.nav<StyledProps>`
position: fixed;
z-index: 2;
top: 0;
right: 0;
transform-origin: right;
transform: ${({ open }) => (open ? 'translateX(0%)' : 'translateX(100%)')};
width: 80%;
height: 100vh;
background: var(--text);
transition: transform 250ms ease-in-out;
display: grid;
grid-template-rows: 30% repeat(4, 50px);
padding: 1rem 1rem 5rem 1rem;
position: fixed;
z-index: 2;
top: 0;
right: 0;
transform-origin: right;
transform: ${({ open }) => (open ? 'translateX(0%)' : 'translateX(100%)')};
width: 80%;
height: 100vh;
background: var(--text);
transition: transform 250ms ease-in-out;
display: grid;
grid-template-rows: 30% repeat(4, 50px);
padding: 1rem 1rem 5rem 1rem;
@media (orientation: landscape) {
grid-template-rows: auto;
}
@media (orientation: landscape) {
grid-template-rows: auto;
}
.close {
justify-self: flex-end;
align-self: flex-start;
margin-top: 0.5rem;
}
.close {
justify-self: flex-end;
align-self: flex-start;
margin-top: 0.5rem;
}
.mobile-button-wrapper {
display: flex;
margin: 0rem 1rem;
.mobile-button-wrapper {
display: flex;
margin: 0rem 1rem;
a {
color: var(--text-inverted) !important;
}
}
a {
color: var(--text-inverted) !important;
}
}
`;
+3 -3
View File
@@ -1,8 +1,8 @@
export type Props = {
open: boolean;
close: () => void;
open: boolean;
close: () => void;
};
export type StyledProps = {
open: boolean;
open: boolean;
};
+42 -40
View File
@@ -8,47 +8,49 @@ import IconButton from '../IconButton';
import MobileNav from '../MobileNav';
const Nav = () => {
const [mobileNavOpen, setMobileNavOpen] = useState<boolean>(false);
const { mode, toggle } = useContext(ThemeContext);
const [mobileNavOpen, setMobileNavOpen] = useState<boolean>(false);
const { mode, toggle } = useContext(ThemeContext);
return (
<Bar>
<Link className='logo' href='/'>
<Image
className='logo-image'
src={mode === 'dark' ? '/light-logo.svg' : '/dark-logo.svg'}
alt='Logo Image'
width={48}
height={48}
/>
<h1>Hazem Krimi</h1>
</Link>
<div className='buttons'>
<IconButton
alt='Theme toggler'
icon={mode === 'dark' ? '/icons/sun.svg' : '/icons/moon.svg'}
onClick={toggle}
/>
<Button href='/projects'>Projects</Button>
<Button href='/blog'>Blog</Button>
<Button href='/contact'>Contact</Button>
<Button href='/resume.pdf' target='_blank' variant='outline'>
Resume
</Button>
</div>
<div className='mobile-buttons'>
<Button href='/resume.pdf' target='_blank' variant='outline'>
Resume
</Button>
<IconButton
alt='Hamburger menu'
icon={mode === 'dark' ? '/icons/light-menu.svg' : '/icons/dark-menu.svg'}
onClick={() => setMobileNavOpen(true)}
/>
</div>
<MobileNav open={mobileNavOpen} close={() => setMobileNavOpen(false)} />
</Bar>
);
return (
<Bar>
<Link className='logo' href='/'>
<Image
className='logo-image'
src={mode === 'dark' ? '/light-logo.svg' : '/dark-logo.svg'}
alt='Logo Image'
width={48}
height={48}
/>
<h1>Hazem Krimi</h1>
</Link>
<div className='buttons'>
<IconButton
alt='Theme toggler'
icon={mode === 'dark' ? '/icons/sun.svg' : '/icons/moon.svg'}
onClick={toggle}
/>
<Button href='/projects'>Projects</Button>
<Button href='/blog'>Blog</Button>
<Button href='/contact'>Contact</Button>
<Button href='/resume.pdf' target='_blank' variant='outline'>
Resume
</Button>
</div>
<div className='mobile-buttons'>
<Button href='/resume.pdf' target='_blank' variant='outline'>
Resume
</Button>
<IconButton
alt='Hamburger menu'
icon={
mode === 'dark' ? '/icons/light-menu.svg' : '/icons/dark-menu.svg'
}
onClick={() => setMobileNavOpen(true)}
/>
</div>
<MobileNav open={mobileNavOpen} close={() => setMobileNavOpen(false)} />
</Bar>
);
};
export default Nav;
+45 -45
View File
@@ -1,59 +1,59 @@
import styled from 'styled-components';
export const Bar = styled.nav`
width: 100%;
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
padding: 1rem 0rem;
width: 100%;
display: grid;
grid-template-columns: auto 1fr;
align-items: center;
padding: 1rem 0rem;
* {
user-select: none;
}
* {
user-select: none;
}
h1 {
font-size: 1.7rem;
h1 {
font-size: 1.7rem;
@media (max-width: 768px) {
font-size: 1rem;
}
}
@media (max-width: 768px) {
font-size: 1rem;
}
}
div,
a.logo {
display: grid;
align-items: center;
column-gap: 1rem;
div,
a.logo {
display: grid;
align-items: center;
column-gap: 1rem;
@media (max-width: 768px) {
column-gap: 0.5rem;
}
}
@media (max-width: 768px) {
column-gap: 0.5rem;
}
}
a.logo {
text-decoration: none;
color: var(--text);
cursor: pointer;
grid-template-columns: repeat(2, auto);
justify-content: flex-start;
}
a.logo {
text-decoration: none;
color: var(--text);
cursor: pointer;
grid-template-columns: repeat(2, auto);
justify-content: flex-start;
}
.buttons {
grid-template-columns: repeat(5, auto);
justify-content: flex-end;
.buttons {
grid-template-columns: repeat(5, auto);
justify-content: flex-end;
@media (max-width: 768px) {
display: none;
}
}
@media (max-width: 768px) {
display: none;
}
}
.mobile-buttons {
display: none;
.mobile-buttons {
display: none;
@media (max-width: 768px) {
display: grid;
grid-template-columns: repeat(2, auto);
justify-content: flex-end;
}
}
@media (max-width: 768px) {
display: grid;
grid-template-columns: repeat(2, auto);
justify-content: flex-end;
}
}
`;