├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── components ├── common │ ├── Blockquote.tsx │ ├── BlogCard.tsx │ ├── ButtonLink.tsx │ ├── CenterBox.tsx │ ├── ContactForm.tsx │ ├── ContainerGrid.tsx │ ├── CustomAnchor.tsx │ ├── CustomAppBar.tsx │ ├── CustomButton.tsx │ ├── CustomChip.tsx │ ├── CustomContainer.tsx │ ├── CustomIconButton.tsx │ ├── CustomSwiper.tsx │ ├── CustomTextField.tsx │ ├── ExpandMoreIconButton.tsx │ ├── FacebookIconLink.tsx │ ├── Filter.tsx │ ├── Gallery.tsx │ ├── HighlightSyntax.tsx │ ├── IconButtonLink.tsx │ ├── ImageWithSkeleton.tsx │ ├── InstagramIconLink.tsx │ ├── Markdown.tsx │ ├── MenuToggler.tsx │ ├── NameLogo.tsx │ ├── ProjectCard.tsx │ ├── Review.tsx │ ├── ScrollDown.tsx │ ├── SkillProgress.tsx │ ├── TextLink.tsx │ ├── TwitterIconLink.tsx │ └── TypingEffect.tsx ├── icon │ ├── ArrowDown.tsx │ ├── HTML5.tsx │ ├── IllustratorCC.tsx │ ├── LightroomCC.tsx │ └── Photoshop.tsx ├── layout │ └── MainLayout.tsx └── section │ ├── About.tsx │ ├── Footer.tsx │ ├── HireMe.tsx │ ├── HomeHero.tsx │ ├── ProjectDetails.tsx │ ├── ProjectNotFound.tsx │ ├── ProjectsSection.tsx │ ├── RecentProjects.tsx │ └── Skills.tsx ├── constants ├── blogPostsData.ts ├── navLinks.tsx ├── pages.tsx └── projectsData.ts ├── context ├── componentsContext.ts └── constantsContext.ts ├── hooks ├── useEventListener.tsx └── useOnClickOutside.tsx ├── lib ├── blogPost.ts └── projects.ts ├── models └── contactFormModel.ts ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── 404.tsx ├── _app.tsx ├── _document.tsx ├── blog.tsx ├── blog │ └── [id].tsx ├── hire-me.tsx ├── index.tsx ├── projects.tsx └── projects │ └── [id].tsx ├── posts ├── blog │ ├── 21-tips-that-every-desinger-must-know.md │ ├── how-to-be-creative-in-design.md │ ├── how-to-shoot-beautiful-photos-in-sunlight.md │ └── tips-for-photographers-when-it-is-raining.md └── projects │ └── bridephotoshoot.mdx ├── public ├── favicon.ico ├── favicon.png ├── posts │ ├── person holding ballpoint pen writing on white paper.jpg │ ├── person holding light bulb.jpg │ ├── water droplets on glass window.jpg │ └── woman doing yoga meditation on brown parquet flooring.jpg ├── profile.jpg ├── projects │ ├── boxWater │ │ ├── boxed-water-is-better-3iiyde9Zj8A-unsplash.jpg │ │ ├── boxed-water-is-better-7mr6Yx-8WLc-unsplash.jpg │ │ ├── boxed-water-is-better-fTwAGXHfXoE-unsplash.jpg │ │ └── boxed-water-is-better-rWQB4e5W5bI-unsplash.jpg │ ├── bride │ │ ├── joeyy-lee--vPL7Ds6wiI-unsplash.jpg │ │ ├── joeyy-lee-0wIWcXIh24M-unsplash.jpg │ │ ├── joeyy-lee-ST2AcGYQ090-unsplash.jpg │ │ ├── joeyy-lee-_Ie8ckXb3WI-unsplash.jpg │ │ ├── joeyy-lee-j0p4xYW2IVw-unsplash.jpg │ │ ├── joeyy-lee-ugNxKaQe9ec-unsplash.jpg │ │ └── joeyy-lee-y56-YywiKUA-unsplash.jpg │ ├── clothing │ │ ├── joeyy-lee-7q8omh36MYA-unsplash.jpg │ │ ├── joeyy-lee-8Vv5H3oIGjA-unsplash.jpg │ │ ├── joeyy-lee-D_KmOD-xDk0-unsplash.jpg │ │ ├── joeyy-lee-mu1v2Ayd6pI-unsplash.jpg │ │ └── joeyy-lee-wZgcKvE0vko-unsplash.jpg │ ├── modeling │ │ ├── joeyy-lee-5CEL1FDPZiM-unsplash.jpg │ │ ├── joeyy-lee-IhuELKvRaUU-unsplash.jpg │ │ ├── joeyy-lee-MEee3-vzeKc-unsplash.jpg │ │ └── joeyy-lee-sBcv_qRnZG8-unsplash.jpg │ └── tube │ │ ├── glenn-claire-DuNXXPScbJM-unsplash.jpg │ │ └── glenn-claire-aYrOtqypmho-unsplash.jpg └── sections │ └── freelancer-male.svg ├── styles ├── components │ ├── Gallery.module.scss │ ├── RecentProjects.module.scss │ ├── ScrollDown.module.css │ └── TypingEffect.module.css ├── globals.css └── theme │ ├── commonThemeOptions.ts │ └── lightTheme.ts ├── tsconfig.json ├── types ├── blogPostType.ts ├── categoryType.ts └── imageType.ts └── utility ├── calcArrayOfObj.ts ├── createBlogPost.ts ├── createEmotionCache.ts └── sortArrayOfObj.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Hosein Pouyanmehr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## theBag 2 | 3 | theBag is a multipurpose template made by [Nextjs](https://nextjs.org/) and [MUI V5](https://mui.com/) which can be used for Portfolios, Resumes, CVs, and Personal websites. 4 | 5 | ## assets 6 | 7 | ### images 8 | 9 | 1. Profile photo: by [Kyle Kempt](https://unsplash.com/@kjkempt17?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/john-doe?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 10 | 11 | ### projects 12 | 13 | 2. Photos in clothing photoshoot project: 14 | 15 | - "woman in white crop top and pink blazer": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 16 | 17 | - "woman in pink and white polka dot crop top and blue denim shorts": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 18 | 19 | - "woman in pink and white floral crop top and blue denim jeans": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 20 | 21 | - "woman in white spaghetti strap top and white skirt": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 22 | 23 | - "woman in black tank top and black skirt holding black leather handbag": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 24 | 25 | 3. Photos in modeling photoshoot project: 26 | 27 | - "woman in gray dress sitting on stairs": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 28 | 29 | - "woman in gray dress leaning on a stone column": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 30 | 31 | - "woman in gray dress walking up stairs": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 32 | 33 | - "woman in gray dress leaning on stair stone railing": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 34 | 35 | 4. Photos in bride photoshoot project: 36 | 37 | - "woman in white tank top wearing silver necklace": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 38 | 39 | - "woman in white wedding dress standing on stairs": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 40 | 41 | - "woman in white sleeveless top wearing silver necklace": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 42 | 43 | - "woman in white wedding dress": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 44 | 45 | - "woman in white tank top holding her hair": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 46 | 47 | - "woman in white sleeveless dress": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 48 | 49 | - "woman in white floral tank top with white flower on ear": Photo by [Joeyy Lee](https://unsplash.com/@joeyy_anne?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 50 | 51 | 5. Photos in box water design project: 52 | 53 | - "Four white water carton on a yellow table": Photo by [Boxed Water Is Better](https://unsplash.com/@boxedwater?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/@boxedwater?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 54 | 55 | - "Two female athlete holding box water in their hands": Photo by [Boxed Water Is Better](https://unsplash.com/@boxedwater?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/@boxedwater?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 56 | 57 | - "Some white water carton on blue table": Photo by [Boxed Water Is Better](https://unsplash.com/@boxedwater?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/@boxedwater?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 58 | 59 | - "Boxed water is Better carton with waves drawn on it with a sharpie": Photo by [Boxed Water Is Better](https://unsplash.com/@boxedwater?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/@boxedwater?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 60 | 61 | 6. Photos in hair mask tube design project: 62 | 63 | - "White and green labeled soft hair mask tube": Photo by [Glenn Claire](https://unsplash.com/@glennclaire?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/@glennclaire?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 64 | 65 | - "Black and White usb hair mask box and tube": Photo by [Glenn Claire](https://unsplash.com/@glennclaire?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/@glennclaire?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 66 | 67 | #### blog 68 | 69 | 1. Photo in "How to shoot beautiful photos in sunlight?" blog post: 70 | 71 | - "woman doing yoga meditation on brown parquet flooring": Photo by [Jared Rice](https://unsplash.com/@jareddrice?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/blog-post-image?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 72 | 73 | 2. Photo in "How to be creative in design?" blog post: 74 | 75 | - "person holding light bulb": Photo by [Diego PH](https://unsplash.com/@jdiegoph?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/blog-post-image?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 76 | 77 | 3. Photo in "21 tips that every desinger must know" blog post: 78 | 79 | - "person holding ballpoint pen writing on white paper": Photo by [STIL](https://unsplash.com/@stilclassics?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/blog-post-image?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 80 | 81 | 4. Photo in "Tips for photographers when it's raining" blog post: 82 | - "water droplets on glass window": Photo by [Frame Harirak](https://unsplash.com/@framemily?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/raindrop?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 83 | 84 | ### icons 85 | 86 | 1. HTML5 icon path by [Iconpacks](https://iconpacks.net/?utm_source=link-attribution&utm_content=6648) 87 | 88 | ### illustrations 89 | 90 | 1. Freelance vectors (both man and woman versions) by [Raftel Design](https://www.vecteezy.com/members/raftel) from [Vecteezy](https://www.vecteezy.com/free-vector/freelance) 91 | 92 | --- 93 | 94 | Made with ❤️ at [mopeim](https://mopeim.com) 95 | -------------------------------------------------------------------------------- /components/common/Blockquote.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Box, BoxProps, styled } from '@mui/material'; 5 | // type 6 | interface BlockquoteProps {} 7 | 8 | const BlockquoteRoot = styled(Box)(({ theme }) => ({ 9 | backgroundColor: 'rgba(255, 255, 255, 0.1)', 10 | padding: '1rem', 11 | borderLeft: `0.25rem solid ${theme.palette.primary.contrastText}`, 12 | borderRadius: theme.shape.borderRadius, 13 | boxShadow: theme.shadows[4], 14 | })); 15 | 16 | const Blockquote: React.FunctionComponent = (props) => { 17 | const { children, ...otherProps } = props; 18 | 19 | return {children}; 20 | }; 21 | 22 | export default Blockquote; 23 | -------------------------------------------------------------------------------- /components/common/BlogCard.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { 5 | Box, 6 | BoxProps, 7 | Card, 8 | CardProps, 9 | CardActions, 10 | CardActionsProps, 11 | Typography, 12 | styled, 13 | } from '@mui/material'; 14 | import ImageWithSkeleton from 'components/common/ImageWithSkeleton'; 15 | import ButtonLink from 'components/common/ButtonLink'; 16 | // type 17 | interface BlogCardProps extends CardProps { 18 | href?: string; 19 | readTime?: number; 20 | imageAlt?: string; 21 | imageSrc?: string; 22 | title?: string; 23 | } 24 | 25 | const CustomCard = styled(Card)(({ theme }) => ({ 26 | padding: '1rem 1rem 0.5rem', 27 | })); 28 | 29 | const CustomCardContent = styled(Box)(({ theme }) => ({ 30 | borderRadius: '4px', 31 | height: '21rem', 32 | marginBottom: '0.5rem', 33 | overflow: 'hidden', 34 | position: 'relative', 35 | width: '100%', 36 | img: { 37 | transform: 'scale(1)', 38 | transition: 'transform 0.5s ease-in-out', 39 | }, 40 | '&: hover': { 41 | img: { 42 | transform: 'scale(1.2)', 43 | transition: 'transform 0.5s ease-in-out', 44 | }, 45 | }, 46 | })); 47 | 48 | const CardTitleWrapper = styled(Box)(({ theme }) => ({ 49 | backgroundColor: 'rgba(255, 255, 255, 0.5)', 50 | borderRadius: theme.shape.borderRadius, 51 | bottom: 0, 52 | height: '40%', 53 | overflow: 'hidden', 54 | padding: '0.5rem', 55 | position: 'absolute', 56 | width: '100%', 57 | })); 58 | 59 | const CustomCardActions = styled(CardActions)( 60 | ({ theme }) => ({ 61 | display: 'flex', 62 | justifyContent: 'space-between', 63 | padding: '0', 64 | }) 65 | ); 66 | 67 | const BlogCard: React.FunctionComponent = (props) => { 68 | const { 69 | children, 70 | href = '#', 71 | readTime = 12, 72 | imageAlt = '', 73 | imageSrc = '/', 74 | title = 'No Title ', 75 | ...otherProps 76 | } = props; 77 | 78 | return ( 79 | 80 | 81 | 87 | 88 | 89 | {title} 90 | 91 | 92 | 93 | 94 | 100 | {readTime} min read 101 | 102 | 103 | read more 104 | 105 | 106 | 107 | ); 108 | }; 109 | 110 | export default BlogCard; 111 | -------------------------------------------------------------------------------- /components/common/ButtonLink.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // next 4 | import Link from 'next/link'; 5 | // custom component 6 | import CustomButton from 'components/common/CustomButton'; 7 | // type 8 | import { ButtonProps } from '@mui/material'; 9 | interface ButtonLinkProps extends ButtonProps { 10 | href?: string; 11 | } 12 | 13 | const CustomButtonWithRef = React.forwardRef( 14 | ({ children, ...otherProps }, ref) => ( 15 | {children} 16 | ) 17 | ); 18 | 19 | CustomButtonWithRef.displayName = 'CustomButtonWithRef'; 20 | 21 | const ButtonLink: React.FunctionComponent = (props) => { 22 | const { children, href = '#', ref, ...otherProps } = props; 23 | 24 | return ( 25 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | export default ButtonLink; 32 | -------------------------------------------------------------------------------- /components/common/CenterBox.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // type 4 | interface CenterBoxProps extends React.CSSProperties { 5 | height?: string | number; 6 | } 7 | 8 | const CenterBox: React.FunctionComponent = (props) => { 9 | const { children, height = '100vh', ...otherProps } = props; 10 | return ( 11 |
20 | {children} 21 |
22 | ); 23 | }; 24 | 25 | export default CenterBox; 26 | -------------------------------------------------------------------------------- /components/common/ContactForm.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // formik 4 | import { Formik } from 'formik'; 5 | // @mui 6 | import { 7 | Card, 8 | CardProps, 9 | CardActions, 10 | CardContent, 11 | CardHeader, 12 | Typography, 13 | styled, 14 | } from '@mui/material'; 15 | // custom component 16 | import CustomTextField from 'components/common/CustomTextField'; 17 | import CustomButton from 'components/common/CustomButton'; 18 | // validation 19 | import { ContactFormSchema } from 'models/contactFormModel'; 20 | // type 21 | interface ContactFormProps {} 22 | 23 | const CustomCard = styled(Card)(({ theme }) => ({ 24 | maxWidth: '32rem', 25 | })); 26 | 27 | const ContactForm: React.FunctionComponent = (props) => { 28 | const toCapitalize = (string: string) => { 29 | return string.charAt(0).toUpperCase() + string.slice(1); 30 | }; 31 | 32 | return ( 33 | 34 | 37 | Contact form 38 | 39 | } 40 | subheader={ 41 | 42 | Lorem ipsum dolor sit amet. 43 | 44 | } 45 | /> 46 | 47 | console.log(values)} 50 | validationSchema={ContactFormSchema} 51 | > 52 | {({ 53 | errors, 54 | touched, 55 | values, 56 | handleChange, 57 | handleBlur, 58 | handleSubmit, 59 | handleReset, 60 | }) => ( 61 | <> 62 | 69 |
70 | 84 | 99 | 115 | 134 | 135 |
136 | 139 | handleReset()} type="reset"> 140 | Reset 141 | 142 | { 144 | let resetForm = true; 145 | handleSubmit(); 146 | for (let item in errors) { 147 | if (item) resetForm = false; 148 | } 149 | if (resetForm) handleReset(); 150 | }} 151 | type="submit" 152 | variant="contained" 153 | > 154 | Submit 155 | 156 | 157 | 158 | )} 159 |
160 |
161 | ); 162 | }; 163 | 164 | export default ContactForm; 165 | -------------------------------------------------------------------------------- /components/common/ContainerGrid.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Grid, GridProps } from '@mui/material'; 5 | // type 6 | interface ContainerGridProps extends GridProps {} 7 | 8 | const ContainerGrid: React.FunctionComponent = (props) => { 9 | const { children, container, spacing, ...otherProps } = props; 10 | 11 | return ( 12 | 13 | {children} 14 | 15 | ); 16 | }; 17 | 18 | export default ContainerGrid; 19 | -------------------------------------------------------------------------------- /components/common/CustomAnchor.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @emotion 4 | import styled from '@emotion/styled'; 5 | // type 6 | interface CustomAnchorProps 7 | extends React.AnchorHTMLAttributes {} 8 | 9 | const Root = styled('a')({ 10 | position: 'relative', 11 | color: 'inherit', 12 | textDecoration: 'none', 13 | cursor: 'pointer', 14 | '&:before': { 15 | backgroundColor: 'currentcolor', 16 | bottom: 0, 17 | content: '""', 18 | display: 'block', 19 | height: '2px', 20 | left: 0, 21 | position: 'absolute', 22 | transform: 'scaleX(0)', 23 | transition: 'transform 0.3s ease', 24 | width: '100%', 25 | }, 26 | '&:hover:before': { 27 | transform: 'scaleX(1)', 28 | }, 29 | ':focus-visible': { 30 | outline: 'none', 31 | ':before': { 32 | transform: 'scaleX(1)', 33 | }, 34 | }, 35 | }); 36 | 37 | const CustomAnchor: React.FunctionComponent = (props) => { 38 | const { children, ...otherProps } = props; 39 | 40 | return {children}; 41 | }; 42 | 43 | export default CustomAnchor; 44 | -------------------------------------------------------------------------------- /components/common/CustomAppBar.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // next 4 | import { useRouter } from 'next/router'; 5 | // @mui 6 | import { 7 | AppBar, 8 | AppBarProps, 9 | Box, 10 | BoxProps, 11 | Container, 12 | Grow, 13 | List, 14 | ListItemButton, 15 | ListItemIcon, 16 | ListItemText, 17 | Toolbar, 18 | ToolbarProps, 19 | styled, 20 | useTheme, 21 | } from '@mui/material'; 22 | // custom component 23 | import NameLogo from 'components/common/NameLogo'; 24 | import ButtonLink from 'components/common/ButtonLink'; 25 | import MenuToggler from 'components/common/MenuToggler'; 26 | import ConstantsContext from 'context/constantsContext'; 27 | import useOnClickOutside from 'hooks/useOnClickOutside'; 28 | import ComponentsContext from 'context/componentsContext'; 29 | import Projects from 'pages/projects'; 30 | // type 31 | interface CustomAppBarProps {} 32 | 33 | const CustomAppBarRoot = styled(AppBar)(({ theme }) => ({ 34 | backgroundColor: 'transparent', 35 | boxShadow: 'none', 36 | })); 37 | 38 | const LinkContainer = styled(Box)(({ theme }) => ({ 39 | marginLeft: theme.direction === 'ltr' ? 'auto' : 'none', 40 | marginRight: theme.direction === 'rtl' ? 'auto' : 'none', 41 | })); 42 | 43 | const LinksBox = styled(Box)(({ theme }) => ({ 44 | '& > :not(a:first-of-type)': { 45 | marginLeft: '0.5rem', 46 | }, 47 | [theme.breakpoints.down('sm')]: { 48 | display: 'none', 49 | }, 50 | })); 51 | 52 | const CustomToolbar = styled(Toolbar)(({ theme }) => ({ 53 | [theme.breakpoints.down('md')]: { 54 | padding: 0, 55 | }, 56 | })); 57 | 58 | const CustomAppBar: React.FunctionComponent = (props) => { 59 | const [dropdownState, setDropdownState] = React.useState(false); 60 | const { containerMaxWidth = 'lg' } = React.useContext(ComponentsContext); 61 | const { navLinks } = React.useContext(ConstantsContext); 62 | navLinks?.sort((a, b) => (a.order > b.order ? 1 : -1)); 63 | 64 | const appBarRef = React.useRef(null); 65 | const handleClickOutside = () => { 66 | if (dropdownState) setDropdownState(false); 67 | }; 68 | 69 | useOnClickOutside(appBarRef, handleClickOutside); 70 | 71 | const router = useRouter(); 72 | const handleDropdownItemClick = (href: string) => { 73 | setDropdownState(false); 74 | router.push(href); 75 | }; 76 | 77 | const { 78 | palette: { background }, 79 | shadows, 80 | } = useTheme(); 81 | 82 | return ( 83 | <> 84 | 85 | 86 | 87 | router.push('/')} 92 | /> 93 | 94 | 95 | {navLinks?.map((navLink, index) => ( 96 | 101 | {navLink.label} 102 | 103 | ))} 104 | 105 | setDropdownState(!dropdownState)} 108 | open={dropdownState} 109 | sx={{ display: { sm: 'none' } }} 110 | /> 111 | 112 | 113 | 114 | 125 | 126 | 127 | {navLinks?.map((navLink, index) => ( 128 | 135 | handleDropdownItemClick(navLink.href)} 137 | sx={{ 138 | borderRadius: '4px', 139 | '&: hover': { 140 | backgroundColor: 'rgba(255, 255, 255, 0.2)', 141 | }, 142 | }} 143 | > 144 | 151 | {navLink.Icon} 152 | 153 | 154 | 155 | 156 | ))} 157 | 158 | 159 | 160 | 161 | 162 | ); 163 | }; 164 | 165 | export default CustomAppBar; 166 | -------------------------------------------------------------------------------- /components/common/CustomButton.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Button, ButtonProps, styled } from '@mui/material'; 5 | // type 6 | interface CustomButtonProps extends ButtonProps {} 7 | 8 | const CustomButtonRoot = styled(Button)(({ theme, variant }) => ({ 9 | textTransform: 'capitalize', 10 | boxShadow: 'none', 11 | '&: hover': { 12 | boxShadow: 'none', 13 | backgroundColor: 'rgba(255, 255, 255, 0.2)', 14 | color: 'currentcolor', 15 | }, 16 | '&: active': { 17 | opacity: 0.5, 18 | transition: '0.3s ease-in-out', 19 | }, 20 | ':focus-visible': { 21 | boxShadow: 'none', 22 | backgroundColor: 'rgba(255, 255, 255, 0.2)', 23 | color: 'currentcolor', 24 | }, 25 | })); 26 | 27 | const CustomButton: React.FunctionComponent = (props) => { 28 | const { children, ...otherProps } = props; 29 | 30 | return ( 31 | 32 | {children} 33 | 34 | ); 35 | }; 36 | 37 | export default CustomButton; 38 | -------------------------------------------------------------------------------- /components/common/CustomChip.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Chip, ChipProps, styled } from '@mui/material'; 5 | // type 6 | interface CustomChipProps extends ChipProps {} 7 | 8 | const ChipRoot = styled(Chip)(({ theme }) => ({ 9 | boxShadow: 'none', 10 | '&: hover': { 11 | boxShadow: 'none', 12 | backgroundColor: 'rgba(255, 255, 255, 0.2)', 13 | color: 'currentcolor', 14 | }, 15 | '&: active': { 16 | opacity: 0.5, 17 | transition: '0.3s ease-in-out', 18 | }, 19 | })); 20 | 21 | const ClickableChip = styled(Chip)(({ theme }) => ({ 22 | boxShadow: 'none', 23 | cursor: 'pointer', 24 | '&: hover': { 25 | boxShadow: 'none', 26 | backgroundColor: 'rgba(255, 255, 255, 0.2)', 27 | color: 'currentcolor', 28 | }, 29 | '&: active': { 30 | opacity: 0.5, 31 | transition: '0.3s ease-in-out', 32 | }, 33 | })); 34 | 35 | const CustomChip: React.FunctionComponent = (props) => { 36 | let { clickable, label, ...otherProps } = props; 37 | 38 | if (otherProps.onClick) clickable = true; 39 | 40 | if (clickable) 41 | return ; 42 | 43 | return ; 44 | }; 45 | 46 | export default CustomChip; 47 | -------------------------------------------------------------------------------- /components/common/CustomContainer.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Container, ContainerProps } from '@mui/material'; 5 | 6 | const CustomContainer: React.FunctionComponent = (props) => { 7 | const { children, maxWidth = 'lg', ...otherProps } = props; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | 16 | export default CustomContainer; 17 | -------------------------------------------------------------------------------- /components/common/CustomIconButton.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { IconButton, IconButtonProps, styled } from '@mui/material'; 5 | // type 6 | interface CustomIconButtonProps extends IconButtonProps {} 7 | 8 | const CustomIconButtonRoot = styled(IconButton)( 9 | ({ theme }) => ({ 10 | ': active': { 11 | opacity: 0.5, 12 | transition: '0.3s ease-in-out', 13 | }, 14 | ':hover': { 15 | backgroundColor: 'rgba(127, 127, 127, 0.1)', 16 | }, 17 | }) 18 | ); 19 | 20 | const CustomIconButton: React.FunctionComponent = ( 21 | props 22 | ) => { 23 | const { children, color = 'inherit', disableRipple, ...otherProps } = props; 24 | 25 | return ( 26 | 27 | {children} 28 | 29 | ); 30 | }; 31 | 32 | export default CustomIconButton; 33 | -------------------------------------------------------------------------------- /components/common/CustomSwiper.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // swiper 4 | import { Pagination, Navigation } from 'swiper'; 5 | import { Swiper, SwiperProps } from 'swiper/react'; 6 | // style 7 | import styles from 'styles/components/RecentProjects.module.scss'; 8 | import { useMediaQuery, useTheme } from '@mui/material'; 9 | // type 10 | interface CustomSwiperProps extends SwiperProps {} 11 | 12 | const CustomSwiper: React.FunctionComponent = (props) => { 13 | const { children, ...otherProps } = props; 14 | const { breakpoints } = useTheme(); 15 | const isUpSm = useMediaQuery(breakpoints.up('sm')); 16 | 17 | return ( 18 | <> 19 |
20 | 44 | {children} 45 | 46 |
47 | 48 | ); 49 | }; 50 | 51 | export default CustomSwiper; 52 | -------------------------------------------------------------------------------- /components/common/CustomTextField.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { TextField, TextFieldProps, styled } from '@mui/material'; 5 | 6 | const CustomTextFieldRoot = styled(TextField)(({ theme }) => ({ 7 | '& .MuiInput-root': { 8 | color: theme.palette.text.secondary, 9 | '&:before': { 10 | borderBottom: '1px solid', 11 | borderBottomColor: theme.palette.text.disabled, 12 | }, 13 | '&:hover:not(.Mui-disabled)::before': { 14 | borderBottom: '2px solid', 15 | borderBottomColor: theme.palette.text.secondary, 16 | }, 17 | }, 18 | '& .MuiFilledInput-root': { 19 | color: theme.palette.text.secondary, 20 | '&:before': { 21 | borderBottom: '1px solid', 22 | borderBottomColor: theme.palette.text.disabled, 23 | }, 24 | '&:hover::before': { 25 | borderBottom: '2px solid', 26 | borderBottomColor: theme.palette.text.secondary, 27 | }, 28 | }, 29 | '& .MuiOutlinedInput-root': { 30 | color: theme.palette.text.secondary, 31 | '& fieldset': { 32 | borderColor: theme.palette.text.disabled, 33 | }, 34 | '&:hover fieldset': { 35 | border: '2px solid', 36 | borderColor: theme.palette.text.secondary, 37 | }, 38 | }, 39 | })); 40 | 41 | const CustomTextField: React.FunctionComponent = (props) => { 42 | const { children, ...otherProps } = props; 43 | 44 | return ; 45 | }; 46 | 47 | export default CustomTextField; 48 | -------------------------------------------------------------------------------- /components/common/ExpandMoreIconButton.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui icons 4 | import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; 5 | // custom component 6 | import CustomIconButton from 'components/common/CustomIconButton'; 7 | // type 8 | import { IconButtonProps } from '@mui/material'; 9 | 10 | interface ExpandMoreIconButtonProps extends IconButtonProps { 11 | open?: boolean; 12 | } 13 | 14 | const ExpandMoreIconButton: React.FunctionComponent = 15 | (props) => { 16 | const { color = 'inherit', open = false, ...otherProps } = props; 17 | 18 | return ( 19 | 20 | 28 | 29 | ); 30 | }; 31 | 32 | export default ExpandMoreIconButton; 33 | -------------------------------------------------------------------------------- /components/common/FacebookIconLink.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui icon 4 | import FacebookIcon from '@mui/icons-material/Facebook'; 5 | // custom component 6 | import IconButtonLink from 'components/common/IconButtonLink'; 7 | // type 8 | import { IconButtonProps } from '@mui/material'; 9 | interface FacebookIconLinkProps extends IconButtonProps { 10 | anchorStyles?: React.CSSProperties; 11 | href?: string; 12 | iconColor?: 13 | | 'inherit' 14 | | 'action' 15 | | 'disabled' 16 | | 'primary' 17 | | 'secondary' 18 | | 'error' 19 | | 'info' 20 | | 'success' 21 | | 'warning'; 22 | iconSize?: 'inherit' | 'large' | 'medium' | 'small' | undefined; 23 | } 24 | 25 | const FacebookIconLink: React.FunctionComponent = ( 26 | props 27 | ) => { 28 | const { 29 | anchorStyles, 30 | href = '#', 31 | iconColor = 'inherit', 32 | iconSize = 'inherit', 33 | ...otherProps 34 | } = props; 35 | 36 | return ( 37 | 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default FacebookIconLink; 44 | -------------------------------------------------------------------------------- /components/common/Filter.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Box, BoxProps, ChipProps, styled } from '@mui/material'; 5 | import CustomChip from 'components/common/CustomChip'; 6 | // type 7 | export interface FilterOption { 8 | active?: boolean; 9 | label: string; 10 | [key: string]: any; 11 | } 12 | interface FilterProps extends ChipProps { 13 | options: FilterOption[]; 14 | onOptionClick?: (a: any) => void; 15 | } 16 | 17 | const FiltersWrapper = styled(Box)(({ theme }) => ({ 18 | display: 'flex', 19 | margin: '1rem', 20 | overflow: 'auto', 21 | [theme.breakpoints.up('sm')]: { 22 | margin: '2rem', 23 | }, 24 | [theme.breakpoints.up('md')]: { 25 | justifyContent: 'center', 26 | }, 27 | div: { 28 | marginBottom: '1rem', 29 | }, 30 | '&> :not(div:last-of-type)': { 31 | marginRight: '0.5rem', 32 | }, 33 | })); 34 | 35 | const Filter: React.FunctionComponent = (props) => { 36 | const { children, onClick, onOptionClick, options, ...otherProps } = props; 37 | const [filterOptions, setFilterOptions] = 38 | React.useState(options); 39 | 40 | const handleOptionClick = (option: FilterOption) => { 41 | const newOptions: FilterOption[] = Object.assign([], filterOptions); 42 | newOptions.forEach((opt) => { 43 | if (opt.active) opt.active = false; 44 | }); 45 | 46 | const index = filterOptions.findIndex((opt) => opt.label === option.label); 47 | newOptions[index].active = true; 48 | 49 | if (onOptionClick) onOptionClick(option); 50 | setFilterOptions(newOptions); 51 | }; 52 | 53 | return ( 54 | 55 | {filterOptions.map((option, index) => ( 56 | handleOptionClick(option)} 62 | {...otherProps} 63 | /> 64 | ))} 65 | 66 | ); 67 | }; 68 | 69 | export default Filter; 70 | -------------------------------------------------------------------------------- /components/common/Gallery.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Box, useMediaQuery, useTheme } from '@mui/material'; 5 | // swiper 6 | import SwiperCore, { Thumbs, Navigation, Scrollbar, FreeMode } from 'swiper'; 7 | import { Swiper, SwiperSlide } from 'swiper/react'; 8 | // custom component 9 | import ImageWithSkeleton from 'components/common/ImageWithSkeleton'; 10 | // style 11 | import classes from 'styles/components/Gallery.module.scss'; 12 | // type 13 | import { ImageProps } from 'types/imageType'; 14 | interface GalleryProps { 15 | images: ImageProps[]; 16 | } 17 | 18 | const Gallery: React.FunctionComponent = (props) => { 19 | const { images } = props; 20 | const [swiperThumbs, setSwiperThumbs] = React.useState(); 21 | const { breakpoints } = useTheme(); 22 | const isUpSm = useMediaQuery(breakpoints.up('sm')); 23 | 24 | return ( 25 | <> 26 | 27 | 33 | {images.map((image, index) => ( 34 | 35 | 41 | 42 | ))} 43 | 44 | 45 | 46 | 53 | {images.map((image, index) => ( 54 | 55 | 62 | 63 | ))} 64 | 65 | 66 | 67 | ); 68 | }; 69 | 70 | export default Gallery; 71 | -------------------------------------------------------------------------------- /components/common/HighlightSyntax.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Box, BoxProps, styled } from '@mui/material'; 5 | // prism-react-renderer 6 | import Highlight, { defaultProps, Language } from 'prism-react-renderer'; 7 | import vsDark from 'prism-react-renderer/themes/vsDark'; 8 | // type 9 | interface HighlightSyntaxProps { 10 | code?: string; 11 | language?: Language; 12 | } 13 | 14 | const PreContainer = styled(Box, { name: 'PreContainer' })( 15 | ({ theme }) => ({ 16 | borderRadius: theme.shape.borderRadius, 17 | padding: '1rem', 18 | }) 19 | ); 20 | 21 | const Pre = styled('pre')>({ 22 | overflowX: 'auto', 23 | }); 24 | 25 | const HighlightSyntax: React.FunctionComponent = ( 26 | props 27 | ) => { 28 | const { 29 | code = `const sayHello = (name:string) => console.log(\`Hello\${name}\`);`, 30 | language = 'tsx', 31 | } = props; 32 | 33 | return ( 34 | 35 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 36 | 37 |
38 |             {tokens.map((line, i) => (
39 |               
40 | {line.map((token, key) => ( 41 | 42 | ))} 43 |
44 | ))} 45 |
46 |
47 | )} 48 |
49 | ); 50 | }; 51 | 52 | export default HighlightSyntax; 53 | -------------------------------------------------------------------------------- /components/common/IconButtonLink.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // next 4 | import Link from 'next/link'; 5 | // custom component 6 | import CustomIconButton from 'components/common/CustomIconButton'; 7 | // type 8 | import { IconButtonProps } from '@mui/material'; 9 | interface IconButtonLinkProps extends IconButtonProps { 10 | href?: string; 11 | } 12 | 13 | const CustomIconButtonWithRef = React.forwardRef< 14 | IconButtonLinkProps, 15 | IconButtonLinkProps 16 | >(({ children, color, ...otherProps }, ref) => { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | }); 23 | 24 | CustomIconButtonWithRef.displayName = 'CustomIconButtonWithRef'; 25 | 26 | const IconButtonLink: React.FunctionComponent = ( 27 | props 28 | ) => { 29 | const { children, href = '#', ref, ...otherProps } = props; 30 | 31 | return ( 32 | 33 | 34 | {children} 35 | 36 | 37 | ); 38 | }; 39 | 40 | export default IconButtonLink; 41 | -------------------------------------------------------------------------------- /components/common/ImageWithSkeleton.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // next 4 | import Image, { ImageProps } from 'next/image'; 5 | import { Skeleton, useTheme } from '@mui/material'; 6 | 7 | const ImageWithSkeleton: React.FunctionComponent = (props) => { 8 | const { children, onLoad, alt = '', ...otherProps } = props; 9 | const [isLoaded, setIsLoaded] = React.useState(false); 10 | const { 11 | palette: { primary }, 12 | } = useTheme(); 13 | 14 | return ( 15 | <> 16 | {alt} setIsLoaded(true)} {...otherProps} /> 17 | {!isLoaded && ( 18 | 26 | )} 27 | 28 | ); 29 | }; 30 | 31 | export default ImageWithSkeleton; 32 | -------------------------------------------------------------------------------- /components/common/InstagramIconLink.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui icon 4 | import InstagramIcon from '@mui/icons-material/Instagram'; 5 | // custom component 6 | import IconButtonLink from 'components/common/IconButtonLink'; 7 | // type 8 | import { IconButtonProps } from '@mui/material'; 9 | interface InstagramIconLinkProps extends IconButtonProps { 10 | anchorStyles?: React.CSSProperties; 11 | href?: string; 12 | iconColor?: 13 | | 'inherit' 14 | | 'action' 15 | | 'disabled' 16 | | 'primary' 17 | | 'secondary' 18 | | 'error' 19 | | 'info' 20 | | 'success' 21 | | 'warning'; 22 | iconSize?: 'inherit' | 'large' | 'medium' | 'small' | undefined; 23 | } 24 | 25 | const InstagramIconLink: React.FunctionComponent = ( 26 | props 27 | ) => { 28 | const { 29 | anchorStyles, 30 | href = '#', 31 | iconColor = 'inherit', 32 | iconSize = 'inherit', 33 | ...otherProps 34 | } = props; 35 | 36 | return ( 37 | 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default InstagramIconLink; 44 | -------------------------------------------------------------------------------- /components/common/Markdown.tsx: -------------------------------------------------------------------------------- 1 | /* eslint react/no-children-prop: 0 */ 2 | // react 3 | import * as React from 'react'; 4 | // next 5 | import Image, { ImageProps } from 'next/image'; 6 | // markdown-to-jsx 7 | import MTJMarkdown from 'markdown-to-jsx'; 8 | // @mui 9 | import { 10 | Box, 11 | BoxProps, 12 | Divider, 13 | DividerProps, 14 | Table, 15 | TableBody, 16 | TableCell, 17 | TableHead, 18 | TableProps, 19 | TableRow, 20 | Typography, 21 | TypographyProps, 22 | styled, 23 | } from '@mui/material'; 24 | // custom component 25 | import TextLink from 'components/common/TextLink'; 26 | import Blockquote from 'components/common/Blockquote'; 27 | import HighlightSyntax from 'components/common/HighlightSyntax'; 28 | // type 29 | interface MarkdownProps { 30 | content?: string; 31 | sx?: React.CSSProperties; 32 | } 33 | 34 | const InlineCode = styled(Box)(({ theme }) => ({ 35 | display: 'inline-block', 36 | padding: '4px 8px', 37 | backgroundColor: 'rgba(255, 255, 255, 0.2)', 38 | borderRadius: theme.shape.borderRadius, 39 | })); 40 | 41 | const PreBlock = ({ children }: { children: any }) => { 42 | if (children && children.props && children.props.component === 'code') { 43 | const lang = children.props.className 44 | ? children.props.className.replace('lang-', '') 45 | : 'tsx'; 46 | 47 | return ; 48 | } 49 | return ( 50 | 51 | {children} 52 | 53 | ); 54 | }; 55 | 56 | const Markdown: React.FunctionComponent = (props) => { 57 | const { content = '**No Content**', sx } = props; 58 | 59 | return ( 60 | 61 | 201 | 202 | ); 203 | }; 204 | 205 | export default Markdown; 206 | -------------------------------------------------------------------------------- /components/common/MenuToggler.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { IconButtonProps } from '@mui/material'; 5 | // @mui/icons-material 6 | import MenuIcon from '@mui/icons-material/Menu'; 7 | import CloseIcon from '@mui/icons-material/Close'; 8 | // custom component 9 | import CustomIconButton from 'components/common/CustomIconButton'; 10 | // type 11 | interface MenuTogglerProps extends IconButtonProps { 12 | open?: boolean; 13 | } 14 | 15 | const MenuToggler: React.FunctionComponent = (props) => { 16 | const { 17 | color = 'primary', 18 | open = false, 19 | size = 'large', 20 | ...otherProps 21 | } = props; 22 | 23 | return ( 24 | 30 | {open ? ( 31 | 32 | ) : ( 33 | 34 | )} 35 | 36 | ); 37 | }; 38 | 39 | export default MenuToggler; 40 | -------------------------------------------------------------------------------- /components/common/NameLogo.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // csstype 4 | import { Property } from 'csstype'; 5 | // type 6 | interface NameLogoProps { 7 | bgBorder?: Property.BorderRadius; 8 | bgColor?: Property.BackgroundColor; 9 | color?: Property.Color; 10 | fontSize?: Property.FontSize; 11 | name?: string; 12 | onClick?: () => void; 13 | } 14 | 15 | const NameLogo: React.FunctionComponent = (props) => { 16 | const { 17 | bgBorder = 4, 18 | bgColor = 'wheat', 19 | color = 'inherit', 20 | fontSize = 'inherit', 21 | name = 'Name', 22 | onClick, 23 | } = props; 24 | return ( 25 |
37 |
47 |

58 | {name} 59 |

60 |
61 | ); 62 | }; 63 | 64 | export default NameLogo; 65 | -------------------------------------------------------------------------------- /components/common/ProjectCard.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // next 4 | import dynamic from 'next/dynamic'; 5 | const Image = dynamic(() => import('next/image')); 6 | // @mui 7 | import { 8 | Box, 9 | BoxProps, 10 | Card, 11 | CardProps, 12 | CardActions, 13 | CardContent, 14 | Icon, 15 | List, 16 | ListItem, 17 | ListItemIcon, 18 | ListItemText, 19 | Skeleton, 20 | Typography, 21 | styled, 22 | useTheme, 23 | } from '@mui/material'; 24 | // @mui icons 25 | import ChevronRightIcon from '@mui/icons-material/ChevronRight'; 26 | import FavoriteIcon from '@mui/icons-material/Favorite'; 27 | // custom component 28 | import CustomButton from 'components/common/CustomButton'; 29 | // type 30 | interface ProjectCardProps extends CardProps { 31 | imageAlt: string; 32 | imageSrc: string; 33 | title: string; 34 | likes: number; 35 | onButtonClick?: () => void; 36 | } 37 | 38 | const CustomCard = styled(Card)(({ theme }) => ({ 39 | img: { 40 | transition: 'all 0.5s ease-in-out', 41 | }, 42 | '&: hover': { 43 | img: { 44 | transition: 'all 0.5s ease-in-out', 45 | transform: 'scale(1.4)', 46 | filter: 'blur(2px)', 47 | WebkitFilter: 'blur(2px)', 48 | }, 49 | }, 50 | })); 51 | 52 | const ImageContainer = styled(Box)(({ theme }) => ({ 53 | position: 'relative', 54 | margin: '1rem', 55 | height: '21rem', 56 | borderRadius: theme.shape.borderRadius, 57 | overflow: 'hidden', 58 | })); 59 | 60 | const ProjectCard: React.FunctionComponent = (props) => { 61 | const { imageAlt, imageSrc, likes, title, onButtonClick, ...otherProps } = 62 | props; 63 | const [isLoaded, setIsLoaded] = React.useState(false); 64 | const { 65 | palette: { primary }, 66 | } = useTheme(); 67 | 68 | return ( 69 | 70 | 71 | {imageAlt} setIsLoaded(true)} 78 | quality={30} 79 | /> 80 | {!isLoaded && ( 81 | 85 | )} 86 | 87 | 88 | 89 | {isLoaded ? ( 90 | title 91 | ) : ( 92 | 93 | )} 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | {isLoaded ? ( 106 | likes 107 | ) : ( 108 | 111 | )} 112 | 113 | 114 | 115 | } 122 | > 123 | more 124 | 125 | 126 | 127 | ); 128 | }; 129 | 130 | export default ProjectCard; 131 | -------------------------------------------------------------------------------- /components/common/Review.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Box, Rating, RatingProps, Typography } from '@mui/material'; 5 | // @mui icons 6 | import FavoriteIcon from '@mui/icons-material/Favorite'; 7 | import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder'; 8 | // type 9 | interface ReviewProps extends RatingProps {} 10 | 11 | const Review: React.FunctionComponent = (props) => { 12 | const { value = 4, ...otherProps } = props; 13 | 14 | return ( 15 | 16 | } 19 | emptyIcon={} 20 | {...otherProps} 21 | /> 22 | 23 | 204 reviews. ({value}/5) 24 | 25 | 26 | ); 27 | }; 28 | 29 | export default Review; 30 | -------------------------------------------------------------------------------- /components/common/ScrollDown.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { Box, BoxProps, styled } from '@mui/material'; 5 | // custom icon 6 | import ArrowDownIcon from 'components/icon/ArrowDown'; 7 | // style 8 | import styles from 'styles/components/ScrollDown.module.css'; 9 | // type 10 | import { Property } from 'csstype'; 11 | interface ScrollDownProps extends BoxProps { 12 | color?: Property.Color; 13 | } 14 | 15 | const AnimationContainer = styled(Box)(({ theme }) => ({ 16 | alignItems: 'center', 17 | display: 'flex', 18 | justifyContent: 'center', 19 | })); 20 | 21 | const ScrollDown: React.FunctionComponent = (props) => { 22 | const { color = 'inherit', ...otherProps } = props; 23 | 24 | return ( 25 | 26 |
27 | 33 |
34 |
35 | ); 36 | }; 37 | 38 | export default ScrollDown; 39 | -------------------------------------------------------------------------------- /components/common/SkillProgress.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { 5 | Box, 6 | CircularProgress, 7 | CircularProgressProps, 8 | Typography, 9 | } from '@mui/material'; 10 | // type 11 | interface SkillProgressProps extends CircularProgressProps { 12 | Icon: React.ReactNode; 13 | subtitle?: string; 14 | } 15 | 16 | const SkillProgress: React.FunctionComponent = (props) => { 17 | const { subtitle, Icon, ...otherProps } = props; 18 | return ( 19 | <> 20 | 28 | 29 | 35 | 47 | {Icon} 48 | 49 | 50 | {subtitle && ( 51 | 52 | {subtitle} 53 | 54 | )} 55 | 56 | 57 | ); 58 | }; 59 | 60 | export default SkillProgress; 61 | -------------------------------------------------------------------------------- /components/common/TextLink.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // next 4 | import Link from 'next/link'; 5 | // custom component 6 | import CustomAnchor from 'components/common/CustomAnchor'; 7 | // type 8 | interface TextLinkProps extends React.AnchorHTMLAttributes {} 9 | 10 | const CustomAnchorWithRef = React.forwardRef( 11 | ({ children, href, onClick, ...otherProps }, ref) => { 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | } 18 | ); 19 | 20 | CustomAnchorWithRef.displayName = 'CustomAnchorWithRef'; 21 | 22 | const TextLink: React.FunctionComponent = (props) => { 23 | const { children, href = '/', ...otherProps } = props; 24 | 25 | return ( 26 | 27 | {children} 28 | 29 | ); 30 | }; 31 | 32 | export default TextLink; 33 | -------------------------------------------------------------------------------- /components/common/TwitterIconLink.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui icon 4 | import TwitterIcon from '@mui/icons-material/Twitter'; 5 | // custom component 6 | import IconButtonLink from 'components/common/IconButtonLink'; 7 | // type 8 | import { IconButtonProps } from '@mui/material'; 9 | interface TwitterIconLinkProps extends IconButtonProps { 10 | anchorStyles?: React.CSSProperties; 11 | href?: string; 12 | iconColor?: 13 | | 'inherit' 14 | | 'action' 15 | | 'disabled' 16 | | 'primary' 17 | | 'secondary' 18 | | 'error' 19 | | 'info' 20 | | 'success' 21 | | 'warning'; 22 | iconSize?: 'inherit' | 'large' | 'medium' | 'small' | undefined; 23 | } 24 | 25 | const TwitterIconLink: React.FunctionComponent = ( 26 | props 27 | ) => { 28 | const { 29 | anchorStyles, 30 | href = '#', 31 | iconColor = 'inherit', 32 | iconSize = 'inherit', 33 | ...otherProps 34 | } = props; 35 | 36 | return ( 37 | 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default TwitterIconLink; 44 | -------------------------------------------------------------------------------- /components/common/TypingEffect.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // react-typing-effect 4 | import ReactTypingEffect, { ReactTypingEffectProps } from 'react-typing-effect'; 5 | // type 6 | interface TypingEffectProps extends ReactTypingEffectProps {} 7 | // style 8 | import styles from 'styles/components/TypingEffect.module.css'; 9 | 10 | const TypingEffect: React.FunctionComponent = (props) => { 11 | const { 12 | eraseDelay = 2000, 13 | eraseSpeed = 100, 14 | speed = 200, 15 | typingDelay = 150, 16 | ...otherProps 17 | } = props; 18 | 19 | return ( 20 | 28 | ); 29 | }; 30 | 31 | export default TypingEffect; 32 | -------------------------------------------------------------------------------- /components/icon/ArrowDown.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import { SvgIcon, SvgIconProps } from '@mui/material'; 5 | // type 6 | import { Property } from 'csstype'; 7 | interface ArrowDownProps extends SvgIconProps { 8 | fill?: Property.Fill; 9 | stroke?: Property.Stroke; 10 | strokeWidth?: Property.StrokeWidth; 11 | } 12 | 13 | const ArrowDown: React.FunctionComponent = (props) => { 14 | const { 15 | fill = 'none', 16 | stroke = 'inherit', 17 | strokeWidth = '4px', 18 | viewBox, 19 | ...otherProps 20 | } = props; 21 | return ( 22 | 23 | 33 | 34 | ); 35 | }; 36 | 37 | export default ArrowDown; 38 | -------------------------------------------------------------------------------- /components/icon/HTML5.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // @mui 4 | import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; 5 | // type 6 | export interface SvgIconRootProps extends SvgIconProps {} 7 | 8 | const SvgIconRoot: React.FunctionComponent = (props) => { 9 | const { 10 | viewBox = '0 0 24 24', 11 | focusable = false, 12 | 'aria-hidden': ariaHidden = true, 13 | ...otherProps 14 | } = props; 15 | 16 | return ( 17 | 23 | 24 | 25 | ); 26 | }; 27 | 28 | export default SvgIconRoot; 29 | -------------------------------------------------------------------------------- /components/icon/IllustratorCC.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // css-types 4 | import { Property } from 'csstype'; 5 | // @mui 6 | import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; 7 | // type 8 | export interface IllustratorCCProps extends SvgIconProps { 9 | bgColor?: Property.Fill; 10 | textColor?: Property.Fill; 11 | } 12 | 13 | const IllustratorCC: React.FunctionComponent = (props) => { 14 | const { 15 | bgColor = '#300', 16 | textColor = '#ff9a00', 17 | viewBox = '0 0 24 24', 18 | focusable = false, 19 | 'aria-hidden': ariaHidden = true, 20 | ...otherProps 21 | } = props; 22 | 23 | return ( 24 | 30 | 34 | 38 | 42 | 43 | ); 44 | }; 45 | 46 | export default IllustratorCC; 47 | -------------------------------------------------------------------------------- /components/icon/LightroomCC.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // css-types 4 | import { Property } from 'csstype'; 5 | // @mui 6 | import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; 7 | // type 8 | export interface LightroomCCProps extends SvgIconProps { 9 | bgColor?: Property.Fill; 10 | textColor?: Property.Fill; 11 | } 12 | 13 | const LightroomCC: React.FunctionComponent = (props) => { 14 | const { 15 | bgColor = '#001e36', 16 | textColor = '#31a8ff', 17 | viewBox = '0 0 24 24', 18 | focusable = false, 19 | 'aria-hidden': ariaHidden = true, 20 | ...otherProps 21 | } = props; 22 | 23 | return ( 24 | 30 | 34 | 35 | 39 | 43 | 44 | 45 | ); 46 | }; 47 | 48 | export default LightroomCC; 49 | -------------------------------------------------------------------------------- /components/icon/Photoshop.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // css-types 4 | import { Property } from 'csstype'; 5 | // @mui 6 | import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; 7 | // type 8 | export interface PhotoshopProps extends SvgIconProps { 9 | bgColor?: Property.Fill; 10 | textColor?: Property.Fill; 11 | } 12 | 13 | const Photoshop: React.FunctionComponent = (props) => { 14 | const { 15 | bgColor = '#001e36', 16 | textColor = '#31a8ff', 17 | viewBox = '0 0 24 24', 18 | focusable = false, 19 | 'aria-hidden': ariaHidden = true, 20 | ...otherProps 21 | } = props; 22 | 23 | return ( 24 | 30 | 34 | 35 | 39 | 43 | 44 | 45 | ); 46 | }; 47 | 48 | export default Photoshop; 49 | -------------------------------------------------------------------------------- /components/layout/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | // react 2 | import * as React from 'react'; 3 | // next 4 | import Head from 'next/head'; 5 | // custom component 6 | import CustomAppBar from 'components/common/CustomAppBar'; 7 | import Footer from 'components/section/Footer'; 8 | // type 9 | import { Page } from 'constants/pages'; 10 | interface MainLayoutProps { 11 | pageData?: Page; 12 | } 13 | 14 | const MainLayout: React.FunctionComponent = (props) => { 15 | const { children, pageData, ...otherProps } = props; 16 | 17 | return ( 18 | <> 19 | {pageData && ( 20 | 21 | {pageData.title ? pageData.title : 'No page title'} 22 | {pageData.description && ( 23 | 24 | )} 25 | 26 | )} 27 | 28 | {children} 29 |