Skip to content

Commit

Permalink
Merge pull request #36 from JongMany/feat/project-modal
Browse files Browse the repository at this point in the history
[Feat] 메인화면 이미지 최적화
  • Loading branch information
JongMany authored Aug 1, 2024
2 parents b10788d + b4762b5 commit d607a26
Show file tree
Hide file tree
Showing 43 changed files with 380 additions and 102 deletions.
5 changes: 3 additions & 2 deletions fe/src/app/layout/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import MainHeader from "@/shared/ui/header/MainHeader";
import ModalProvider from "@/shared/ui/provider/ModalProvider";
import { Outlet, useLocation } from "react-router-dom";

export function MainLayout() {
Expand All @@ -7,11 +8,11 @@ export function MainLayout() {
const outletStyle = location.pathname !== "/" ? "pt-[8dvh]" : "";

return (
<>
<ModalProvider>
<MainHeader />
<div className={outletStyle}>
<Outlet />
</div>
</>
</ModalProvider>
);
}
6 changes: 3 additions & 3 deletions fe/src/entities/introduction/ui/Introduction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export default function Introdcution() {
});

return (
<div className="flex flex-col px-2 w-[70vw] items-center justify-center min-h-[80vh]">
<h1 className="py-6 text-4xl">방구석 코딩쟁이, 이종민입니다.</h1>
<div className="flex flex-col items-start gap-y-3">
<div className="flex flex-col px-2 w-[70vw] items-center justify-center min-h-[80vh] relative -top-[10vh]">
<h1 className="py-6 mb-10 text-4xl">방구석 코딩쟁이, 이종민입니다.</h1>
<div className="flex flex-col items-start gap-y-3 min-h-[30vh]">
{typingText.map((text) => (
<p key={text} className="text-lg ">
{text}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { useDeviceSize } from "@/shared/libs";

type Props = {
fileUrl: string;
className?: string;
};

export default function FileDownload({ fileUrl }: Props) {
export default function FileDownload({ fileUrl, className = "" }: Props) {
const { isDownloading, handleFileDownload } = useDownloadFile(fileUrl);
const device = useDeviceSize();
const textStyle = device === "desktop" ? "text-lg" : "text-sm";
Expand All @@ -14,7 +15,7 @@ export default function FileDownload({ fileUrl }: Props) {
<button
onClick={handleFileDownload}
disabled={isDownloading}
className={`font-semibold text-white px-2 py-1 rounded-md border-[1px] mb-4 ${textStyle}`}
className={`font-semibold text-white px-2 py-1 rounded-md border-[1px] mb-4 ${textStyle} ${className}`}
>
<span>{isDownloading ? "다운로드 중" : "다운로드하기"}</span>
{/* <form action={fileUrl} method="get" target="_blank">
Expand Down
27 changes: 27 additions & 0 deletions fe/src/entities/projects/ui/modal/ProjectDetailModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { projectOutlineList } from "@/shared/constants";
import { Modal } from "@/shared/ui";

type Props = {
closeModal: () => void;
projectName: string;
};

export const ProjectDetailModal = ({ closeModal, projectName }: Props) => {
const project = projectOutlineList.find(
(project) => project.name === projectName
);

return (
<Modal>
<div className="w-[700px] h-[600px] backdrop-blur-[60px] text-white rounded-[5px]">
<div className="flex justify-end px-4 py-2">
<button onClick={closeModal}>닫기</button>
</div>
<section>
<h1 className="mb-2 text-2xl text-center">{project?.name}</h1>
<article className="overflow-x-scroll scrollbar-hide"></article>
</section>
</div>
</Modal>
);
};
33 changes: 31 additions & 2 deletions fe/src/entities/projects/ui/project/DetailLink.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,44 @@
import { useDeviceSize } from "@/shared/libs";
import { ProjectDetailModal } from "@/entities/projects/ui/modal/ProjectDetailModal";
import { useDeviceSize, useModal } from "@/shared/libs";
import { Link } from "react-router-dom";

type Props = {
projectName: string;
};
export const DetailLink = ({ projectName }: Props) => {
const device = useDeviceSize();
const { closeModal, openModal } = useModal(
() => {
return (
<ProjectDetailModal closeModal={closeModal} projectName={projectName} />
);
},
{
onOpen: () => {
const container = document.querySelector(
'[data-name="project-showcase"]'
);
if (container) {
(container as HTMLElement).style.overflow = "hidden";
}
},
onClose: () => {
const container = document.querySelector(
'[data-name="project-showcase"]'
);
if (container) {
// scroll-snap 을 위해 auto로 변경
(container as HTMLElement).style.overflow = "auto";
}
},
}
);
// PC 환경이 아닌 경우는 URL로 이동
if (device !== "desktop") {
const encodedProjectName = encodeURIComponent(projectName);
return <Link to={`/project/${encodedProjectName}`}>더 자세히 보기</Link>;
}

return <Link to="/project">더 자세히 보기</Link>;
// PC 환경에서는 모달로 띄움
return <button onClick={openModal}>더 자세히 보기</button>;
};
58 changes: 14 additions & 44 deletions fe/src/entities/projects/ui/project/ImageContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,28 @@
import { useDeviceSize } from "@/shared/libs";
import "./ImageContainer.css";
import { useEffect, useRef, useState } from "react";
import { Img } from "@/shared/ui/image/Img";

// https://blog.webdevsimplified.com/2023-05/lazy-load-images/
export const ImageContainer = ({
image,
alt = "project",
smallImageUrl,
imageSet,
}: {
image: string;
alt?: string;
smallImageUrl: string;
imageSet: {
loadImageUrl: string;
smallImageUrl: string;
mediumImageUrl: string;
largeImageUrl: string;
originImageUrl: string;
};
}) => {
const device = useDeviceSize();
const imageStyle = device === "desktop" ? "w-full" : "w-[80vw] h-[34vh]";
const blurredImageDivRef = useRef<HTMLDivElement>(null);
const imageRef = useRef<HTMLImageElement>(null);
const [, setIsLargeImgLoaded] = useState(false);

useEffect(() => {
function loaded() {
if (blurredImageDivRef.current) {
setIsLargeImgLoaded(true);
blurredImageDivRef.current.classList.add("loaded");
}
}

const img = imageRef.current;
if (img?.complete) {
loaded();
} else {
img?.addEventListener("load", loaded);
}

return () => {
img?.removeEventListener("load", loaded);
};
}, []);

const containerStyle = device === "desktop" ? "w-[100%]" : "w-[70%] h-[80%]";
return (
<div className="basis-[50%] flex flex-col items-center justify-center">
<div
style={{ backgroundImage: `url(${smallImageUrl})` }}
className={`blurred-img`}
ref={blurredImageDivRef}
>
<img
ref={imageRef}
className={`${imageStyle}`}
src={image}
alt={alt}
loading="lazy"
/>
<section className="basis-[50%] flex flex-col items-center justify-center">
<div className={containerStyle}>
<Img imageStyle={imageStyle} alt={alt} imageSet={imageSet} />
</div>
</div>
</section>
);
};
6 changes: 3 additions & 3 deletions fe/src/entities/projects/ui/project/ProjectContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ export default function ProjectContainer({
}: PropsWithChildren<Props>) {
const device = useDeviceSize();

const rowAlign = device === "desktop" ? "row" : "col";
const alignStyle = getAlignStyle(rowAlign, isAlignReverse);

const containerRef = useRef<HTMLElement>(null);
const isInView = useInView(containerRef, {});

const rowAlign = device === "desktop" ? "row" : "col";
const alignStyle = getAlignStyle(rowAlign, isAlignReverse);

return (
<motion.article
className="flex h-[70vh] items-center justify-center"
Expand Down
1 change: 0 additions & 1 deletion fe/src/shared/assets/images/react.svg

This file was deleted.

Binary file added fe/src/shared/assets/img_large/crypto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_large/eyeve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_large/portfolio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_large/ready_to_work.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_large/star.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_large/study-log.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_medium/crypto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_medium/eyeve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_medium/portfolio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_medium/ready_to_work.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_medium/star.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_medium/study-log.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_small/crypto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_small/eyeve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_small/portfolio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_small/ready_to_work.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_small/star.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fe/src/shared/assets/img_small/study-log.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
107 changes: 75 additions & 32 deletions fe/src/shared/constants/projects/projectOutlineList.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
import studyLogProjectImg from "@/shared/assets/images/study-log.png";
import studyLogProjectSmallImg from "@/shared/assets/images/study-log-small.png";
import readyToWorkProjectImg from "@/shared/assets/images/ready_to_work.png";
import readyToWorkProjectSmallImg from "@/shared/assets/images/ready_to_work-small.png";
import cryptoProjectImg from "@/shared/assets/images/crypto.png";
import cryptoProjectSmallImg from "@/shared/assets/images/crypto-small.png";
import eyeveProjectImg from "@/shared/assets/images/eyeve.png";
import eyeveProjectSmallImg from "@/shared/assets/images/eyeve-small.png";
import vscodeExtensionProjectImg from "@/shared/assets/images/vscode-extension.png";
import vscodeExtensionProjectSmallImg from "@/shared/assets/images/vscode-extension-small.png";
import portfolioProjectImg from "@/shared/assets/images/portfolio.png";
import portfolioProjectSmallImg from "@/shared/assets/images/portfolio-small.png";
import type { Project, ProjectStyle } from "@/shared/model";
import readyToWorkProjectMediumImg from "@/shared/assets/img_medium/ready_to_work.png";
import readyToWorkProjectSmallImg from "@/shared/assets/img_small/ready_to_work.png";
import readyToWorkProjectLargeImg from "@/shared/assets/img_large/ready_to_work.png";
import readyToWorkProjectLoadImg from "@/shared/assets/images/ready_to_work-small.png";
import readyToWorkProjectOriginImg from "@/shared/assets/images/ready_to_work.png";

const readyToWork: Project & ProjectStyle = {
import studyLogProjectMediumImg from "@/shared/assets/img_medium/study-log.png";
import studyLogProjectSmallImg from "@/shared/assets/img_small/study-log.png";
import studyLogProjectLargeImg from "@/shared/assets/img_large/study-log.png";
import studyLogProjectLoadImg from "@/shared/assets/images/study-log-small.png";
import studyLogProjectOriginImg from "@/shared/assets/images/study-log.png";

import cryptoProjectMediumImg from "@/shared/assets/img_medium/crypto.png";
import cryptoProjectSmallImg from "@/shared/assets/img_small/crypto.png";
import cryptoProjectLargeImg from "@/shared/assets/img_large/crypto.png";
import cryptoProjectLoadImg from "@/shared/assets/images/crypto-small.png";
import cryptoProjectOriginImg from "@/shared/assets/images/crypto.png";

import eyeveProjectMediumImg from "@/shared/assets/img_medium/eyeve.png";
import eyeveProjectSmallImg from "@/shared/assets/img_small/eyeve.png";
import eyeveProjectLargeImg from "@/shared/assets/img_large/eyeve.png";
import eyeveProjectLoadImg from "@/shared/assets/images/eyeve-small.png";
import eyeveProjectOriginImg from "@/shared/assets/images/eyeve.png";

import vscodeExtensionProjectMediumImg from "@/shared/assets/img_medium/vscode-extension.png";
import vscodeExtensionProjectSmallImg from "@/shared/assets/img_small/vscode-extension.png";
import vscodeExtensionProjectLargeImg from "@/shared/assets/img_large/vscode-extension.png";
import vscodeExtensionProjectLoadImg from "@/shared/assets/images/vscode-extension-small.png";
import vscodeExtensionProjectOriginImg from "@/shared/assets/images/vscode-extension.png";

import portfolioProjectMediumImg from "@/shared/assets/img_medium/portfolio.png";
import portfolioProjectSmallImg from "@/shared/assets/img_small/portfolio.png";
import portfolioProjectLargeImg from "@/shared/assets/img_large/portfolio.png";
import portfolioProjectLoadImg from "@/shared/assets/images/portfolio-small.png";
import portfolioProjectOriginImg from "@/shared/assets/images/portfolio.png";

import type { ProjectOutline, ProjectStyle } from "@/shared/model";

const readyToWork: ProjectOutline & ProjectStyle = {
image: {
default: readyToWorkProjectImg,
small: readyToWorkProjectSmallImg,
// default: readyToWorkProjectImg,
smallImageUrl: readyToWorkProjectSmallImg,
mediumImageUrl: readyToWorkProjectMediumImg,
largeImageUrl: readyToWorkProjectLargeImg,
loadImageUrl: readyToWorkProjectLoadImg,
originImageUrl: readyToWorkProjectOriginImg,
},
name: "Ready To Work",
description:
Expand All @@ -34,10 +62,13 @@ const readyToWork: Project & ProjectStyle = {
animeDirection: "LToR",
};

const eyeve: Project & ProjectStyle = {
const eyeve: ProjectOutline & ProjectStyle = {
image: {
default: eyeveProjectImg,
small: eyeveProjectSmallImg,
smallImageUrl: eyeveProjectSmallImg,
mediumImageUrl: eyeveProjectMediumImg,
largeImageUrl: eyeveProjectLargeImg,
loadImageUrl: eyeveProjectLoadImg,
originImageUrl: eyeveProjectOriginImg,
},
name: "Eyeve",
description:
Expand All @@ -59,10 +90,13 @@ const eyeve: Project & ProjectStyle = {
},
};

const cryptoChart: Project & ProjectStyle = {
const cryptoChart: ProjectOutline & ProjectStyle = {
image: {
default: cryptoProjectImg,
small: cryptoProjectSmallImg,
smallImageUrl: cryptoProjectSmallImg,
mediumImageUrl: cryptoProjectMediumImg,
largeImageUrl: cryptoProjectLargeImg,
loadImageUrl: cryptoProjectLoadImg,
originImageUrl: cryptoProjectOriginImg,
},
name: "암호화폐 차트 그래프",
description:
Expand All @@ -82,10 +116,13 @@ const cryptoChart: Project & ProjectStyle = {
animeDirection: "RToL",
};

const studyLog: Project & ProjectStyle = {
const studyLog: ProjectOutline & ProjectStyle = {
image: {
default: studyLogProjectImg,
small: studyLogProjectSmallImg,
smallImageUrl: studyLogProjectSmallImg,
mediumImageUrl: studyLogProjectMediumImg,
largeImageUrl: studyLogProjectLargeImg,
loadImageUrl: studyLogProjectLoadImg,
originImageUrl: studyLogProjectOriginImg,
},
name: "웹 기반 스터디 인증 SNS",
description:
Expand All @@ -102,10 +139,13 @@ const studyLog: Project & ProjectStyle = {
animeDirection: "LToR",
};

const vscodeExtension: Project & ProjectStyle = {
const vscodeExtension: ProjectOutline & ProjectStyle = {
image: {
default: vscodeExtensionProjectImg,
small: vscodeExtensionProjectSmallImg,
smallImageUrl: vscodeExtensionProjectSmallImg,
mediumImageUrl: vscodeExtensionProjectMediumImg,
largeImageUrl: vscodeExtensionProjectLargeImg,
loadImageUrl: vscodeExtensionProjectLoadImg,
originImageUrl: vscodeExtensionProjectOriginImg,
},
name: "StudyLog VSCode Extension",
description:
Expand All @@ -126,10 +166,13 @@ const vscodeExtension: Project & ProjectStyle = {
animeDirection: "RToL",
};

const portfolio: Project & ProjectStyle = {
const portfolio: ProjectOutline & ProjectStyle = {
image: {
default: portfolioProjectImg,
small: portfolioProjectSmallImg,
smallImageUrl: portfolioProjectSmallImg,
mediumImageUrl: portfolioProjectMediumImg,
largeImageUrl: portfolioProjectLargeImg,
loadImageUrl: portfolioProjectLoadImg,
originImageUrl: portfolioProjectOriginImg,
},
name: "포트폴리오 사이트",
description:
Expand All @@ -147,7 +190,7 @@ const portfolio: Project & ProjectStyle = {
animeDirection: "LToR",
};

export const projectOutlineList: (Project & ProjectStyle)[] = [
export const projectOutlineList: (ProjectOutline & ProjectStyle)[] = [
readyToWork,
eyeve,
cryptoChart,
Expand Down
1 change: 1 addition & 0 deletions fe/src/shared/libs/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./typing/useTypingAnimation";
export * from "./media-query/useDeviceSize";
export * from "./modal/useModal";
4 changes: 2 additions & 2 deletions fe/src/shared/libs/media-query/useDeviceSize.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useMediaQuery } from "@/shared/libs/media-query/useMediaQuery";

export function useDeviceSize() {
const isDesktopQuery = "(min-width: 1024px)";
const isTabletQuery = "(min-width: 768px) and (max-width: 1023px)";
const isDesktopQuery = "(min-width: 840px)";
const isTabletQuery = "(min-width: 768px) and (max-width: 840px)";
const isMobileQuery = "(max-width: 767px)";

const isDesktop = useMediaQuery(isDesktopQuery);
Expand Down
Loading

0 comments on commit d607a26

Please sign in to comment.