Skip to content

Commit

Permalink
Merge pull request #35 from JongMany/feat/project-modal
Browse files Browse the repository at this point in the history
[Feat] Modal 생성 기능 추가
  • Loading branch information
JongMany authored Jul 31, 2024
2 parents 50b4ba6 + cbc74fc commit b10788d
Show file tree
Hide file tree
Showing 21 changed files with 443 additions and 385 deletions.
20 changes: 10 additions & 10 deletions fe/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
module.exports = {
root: true,
root: false,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser",
plugins: ["react-refresh"],
rules: {
'react-refresh/only-export-components': [
'warn',
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
}
};
10 changes: 8 additions & 2 deletions fe/src/app/layout/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import MainHeader from "@/shared/ui/header/MainHeader";
import { Outlet } from "react-router-dom";
import { Outlet, useLocation } from "react-router-dom";

export function MainLayout() {
const location = useLocation();
// Header의 높이만큼 Outlet을 내리기 위한 스타일
const outletStyle = location.pathname !== "/" ? "pt-[8dvh]" : "";

return (
<>
<MainHeader />
<Outlet />
<div className={outletStyle}>
<Outlet />
</div>
</>
);
}
11 changes: 11 additions & 0 deletions fe/src/app/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import ErrorLayout from "../layout/ErrorLayout";
import { MainLayout } from "@/app/layout/MainLayout";
import MainPage from "@/pages/main/index";
const NotFoundPage = lazy(() => import("@/pages/not-found/index"));
const ProjectDetailPage = lazy(
() => import("@/pages/project/projectName/index")
);

// const MainPage = lazy(() => import("../pages/main/MainPage"));

Expand All @@ -18,6 +21,14 @@ const router = createBrowserRouter([
path: "main",
element: <Suspense fallback={null}>{/* <MainPage /> */}</Suspense>,
},
{
path: "project/:projectName",
element: (
<Suspense fallback={null}>
<ProjectDetailPage />
</Suspense>
),
},
{
path: "*",
element: <Suspense fallback={null}>{<NotFoundPage />}</Suspense>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { useDownloadFile } from "@/entities/projects/libs/useDownloadFile";
import { useDeviceSize } from "@/shared/libs";

type Props = {
fileUrl: string;
};

export default function FileDownload({ fileUrl }: Props) {
const { isDownloading, handleFileDownload } = useDownloadFile(fileUrl);
const device = useDeviceSize();
const textStyle = device === "desktop" ? "text-lg" : "text-sm";

return (
<button
onClick={handleFileDownload}
disabled={isDownloading}
className="font-semibold text-white px-2 py-1 rounded-md border-[1px] mt-4"
className={`font-semibold text-white px-2 py-1 rounded-md border-[1px] mb-4 ${textStyle}`}
>
<span>{isDownloading ? "다운로드 중" : "다운로드하기"}</span>
{/* <form action={fileUrl} method="get" target="_blank">
Expand Down
79 changes: 79 additions & 0 deletions fe/src/entities/projects/ui/project/Description.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useDeviceSize } from "@/shared/libs";
import { useInView } from "framer-motion";
import { PropsWithChildren, useRef } from "react";
import { DetailLink } from "@/entities/projects/ui/project/DetailLink";

type DescriptionProps = {
projectName: string;
projectDescription: string;
techSkills: string[];
animeDirection: "LToR" | "RToL";
};

export const Description = ({
projectName,
projectDescription,
techSkills,
animeDirection,
children,
}: PropsWithChildren<DescriptionProps>) => {
const device = useDeviceSize();
const containerRef = useRef<HTMLDivElement>(null);
const isInView = useInView(containerRef, {});

const headTextFontStyle = device === "desktop" ? "text-xl" : "text-lg";
const detailTextFontStyle = device === "desktop" ? "text-sm" : "text-xs";

const selectedTechSkills =
device === "desktop" ? techSkills : [...techSkills.slice(0, 3), "..."];

return (
<div
className="flex flex-col items-start justify-center basis-[50%] backdrop-blur-sm px-4 py-2"
ref={containerRef}
>
<h1 className="flex items-center justify-center w-full mb-4 text-lg">
<span className="pr-2 mr-4 border-r-2 max-w-[40%] font-bold">
프로젝트 명{" "}
</span>
<span className={`flex-1 text-2xl ${headTextFontStyle}`}>
{projectName}
</span>
</h1>
<p className="flex flex-col flex-1 mb-4">
<span className={`${headTextFontStyle} font-semibold mb-2`}>
프로젝트 소개
</span>
<span className={`ml-4 ${detailTextFontStyle}`}>
{projectDescription}
</span>
</p>
<div className="mb-4">
<p className="mb-2 font-semibold">사용한 기술 스택</p>
<ul className={`flex flex-col ml-4 gap-y-1 ${detailTextFontStyle}`}>
{selectedTechSkills.map((skill, idx) => (
<li
key={skill}
style={{
transform: isInView
? "none"
: `translateX(${
animeDirection === "LToR" ? "-300px" : "300px"
})`,
opacity: isInView ? 1 : 0,
transition: `all 0.5s cubic-bezier(0.17, 0.55, 0.55, 1) `,
transitionDelay: `${idx * 0.25 + 1.2}s`,
}}
>
{skill}
</li>
))}
</ul>
</div>
<div>{children}</div>
<p>
<DetailLink projectName={projectName} />
</p>
</div>
);
};
15 changes: 15 additions & 0 deletions fe/src/entities/projects/ui/project/DetailLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useDeviceSize } from "@/shared/libs";
import { Link } from "react-router-dom";

type Props = {
projectName: string;
};
export const DetailLink = ({ projectName }: Props) => {
const device = useDeviceSize();
if (device !== "desktop") {
const encodedProjectName = encodeURIComponent(projectName);
return <Link to={`/project/${encodedProjectName}`}>더 자세히 보기</Link>;
}

return <Link to="/project">더 자세히 보기</Link>;
};
58 changes: 58 additions & 0 deletions fe/src/entities/projects/ui/project/ImageContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useDeviceSize } from "@/shared/libs";
import "./ImageContainer.css";
import { useEffect, useRef, useState } from "react";

// https://blog.webdevsimplified.com/2023-05/lazy-load-images/
export const ImageContainer = ({
image,
alt = "project",
smallImageUrl,
}: {
image: string;
alt?: string;
smallImageUrl: 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);
};
}, []);

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"
/>
</div>
</div>
);
};
Loading

0 comments on commit b10788d

Please sign in to comment.