Skip to content

Commit

Permalink
Merge branch 'develop/fe' into feature/fe/#357
Browse files Browse the repository at this point in the history
  • Loading branch information
0jenn0 authored Aug 21, 2024
2 parents fd88b7f + ef2ab4f commit 5d28555
Show file tree
Hide file tree
Showing 47 changed files with 1,112 additions and 168 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,60 @@
import { css } from "@emotion/react";
import styled from "@emotion/styled";

import theme from "@styles/theme";
import { PRIMITIVE_COLORS } from "@styles/tokens";

export const registerButtonStyle = css`
export const FloatingButtonContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
align-items: flex-end;
position: fixed;
right: max(2rem, calc(50% - 48rem / 2 + 2rem));
right: max(0vw + 2rem, calc(50vw - 22rem));
bottom: 2rem;
z-index: ${theme.zIndex.floatingButton};
z-index: ${({ theme }) => theme.zIndex.floatingButton};
gap: ${({ theme }) => theme.spacing.s};
`;

export const SubButtonContainer = styled.div<{ $isOpen: boolean }>`
display: flex;
flex-direction: column;
align-items: flex-start;
width: 15rem;
padding: ${({ theme }) => theme.spacing.l} ${({ theme }) => theme.spacing.m};
border-radius: ${({ theme }) => theme.spacing.s};
background-color: ${PRIMITIVE_COLORS.gray[700]};
gap: ${({ theme }) => theme.spacing.l};
transition: all 0.3s ease-out;
${({ $isOpen }) => css`
opacity: ${$isOpen ? 1 : 0};
transform: translateY(${$isOpen ? 0 : 2}rem);
pointer-events: ${$isOpen ? "auto" : "none"};
`}
`;

export const MainButtonWrapper = styled.div<{ $isOpen: boolean }>`
display: flex;
justify-content: center;
align-items: center;
width: 6rem;
height: 6rem;
border-radius: 50%;
box-shadow: 0 2px 4px rgb(0 0 0 / 20%);
background-color: ${theme.colors.primary};
background-color: ${({ $isOpen, theme }) =>
$isOpen ? PRIMITIVE_COLORS.gray[700] : theme.colors.primary};
transition: transform 0.3s ease-out;
transform: ${({ $isOpen }) => ($isOpen ? "rotate(45deg)" : "rotate(0)")};
cursor: pointer;
`;

export const subButtonTextStyle = css`
color: ${PRIMITIVE_COLORS.white};
`;
39 changes: 32 additions & 7 deletions frontend/src/components/common/FloatingButton/FloatingButton.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";

import { ROUTE_PATHS_MAP } from "@constants/route";

import { PRIMITIVE_COLORS } from "@styles/tokens";

import IconButton from "../IconButton/IconButton";
import Modal from "../Modal/Modal";
import Text from "../Text/Text";
import * as S from "./FloatingButton.styled";

const FloatingButton = () => {
const [isOpen, setIsOpen] = useState(false);
const navigate = useNavigate();

const handleClickRegisterButton = () => {
const handleToggleButton = () => {
setIsOpen((prev) => !prev);
};

const handleClickTravelogueRegister = () => {
navigate(ROUTE_PATHS_MAP.travelogueRegister);
};

const handleClickTravelPlanRegister = () => {
navigate(ROUTE_PATHS_MAP.travelPlanRegister);
};

return (
<IconButton
iconType="edit"
color={PRIMITIVE_COLORS.white}
onClick={handleClickRegisterButton}
css={S.registerButtonStyle}
/>
<S.FloatingButtonContainer>
{isOpen && <Modal isOpen={isOpen} onCloseModal={handleToggleButton} />}
<S.SubButtonContainer $isOpen={isOpen}>
<button onClick={handleClickTravelPlanRegister}>
<Text textType="body" css={S.subButtonTextStyle}>
✈️ 여행 계획 작성
</Text>
</button>
<button onClick={handleClickTravelogueRegister}>
<Text textType="body" css={S.subButtonTextStyle}>
📝 여행기 작성
</Text>
</button>
</S.SubButtonContainer>

<S.MainButtonWrapper onClick={handleToggleButton} $isOpen={isOpen}>
<IconButton iconType="plus" color={PRIMITIVE_COLORS.white} size="20" />
</S.MainButtonWrapper>
</S.FloatingButtonContainer>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { Input } from "@components/common/Input/Input.styled";

import { ROUTE_PATHS_MAP } from "@constants/route";

import { extractLastPath } from "@utils/extractId";

import Header from "../Header";
import * as S from "./SearchHeader.styled";

Expand All @@ -21,7 +23,7 @@ const SearchHeader = () => {

const encodedKeyword =
location.pathname.split("/").length > MIN_KEYWORD_LENGTH
? location.pathname.split("/").pop()
? extractLastPath(location.pathname)
: "";

const receivedKeyword = encodedKeyword ? decodeURIComponent(encodedKeyword) : "";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import styled from "@emotion/styled";

export const BackDrop = styled.div`
position: absolute;
position: fixed;
inset: 0;
z-index: ${({ theme }) => theme.zIndex.overlay};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const Default: Story = {
args: {
mainText: "여행기를 등록할까요?",
subText: "등록한 후에도 다시 여행기를 수정할 수 있어요.",
secondaryButtonLabel: "취소",
primaryButtonLabel: "확인",
isOpen: true,
onClose: () => {},
onConfirm: () => {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export interface ModalBottomSheetProps {
mainText: string;
subText: string;
isOpen: boolean;
primaryButtonLabel: string;
secondaryButtonLabel: string;
onClose: () => void;
onConfirm: () => void;
}
Expand All @@ -19,6 +21,8 @@ const ModalBottomSheet = ({
mainText,
subText,
isOpen,
primaryButtonLabel,
secondaryButtonLabel,
onClose,
onConfirm,
}: ModalBottomSheetProps) => {
Expand All @@ -32,10 +36,10 @@ const ModalBottomSheet = ({
<Content mainText={mainText} subText={subText} />
<Footer>
<Button variants="secondary" onClick={onClose}>
취소
{secondaryButtonLabel}
</Button>
<Button variants="primary" onClick={onConfirm}>
확인
{primaryButtonLabel}
</Button>
</Footer>
</Container>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import styled from "@emotion/styled";

import { SPACING } from "@styles/tokens";

export const Layout = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: calc(100vh - 6rem);
gap: ${({ theme }) => theme.spacing.l};
gap: ${SPACING.l};
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
`;

export const TextContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
gap: ${({ theme }) => theme.spacing.m};
gap: ${SPACING.m};
`;
2 changes: 2 additions & 0 deletions frontend/src/components/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ export { default as Checkbox } from "./Checkbox/Checkbox";
export { default as Chip } from "./Chip/Chip";
export { default as TextField } from "./TextField/TextField";
export { default as Calendar } from "./Calendar/Calendar";
export { default as SearchFallback } from "./SearchFallback/SearchFallback";
export { default as FloatingButton } from "./FloatingButton/FloatingButton";
6 changes: 3 additions & 3 deletions frontend/src/components/layout/AppLayout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { DefaultHeader, HomePageHeader, SearchHeader } from "@components/common"

import { ROUTE_PATHS_MAP } from "@constants/route";

import { extractLastPath } from "@utils/extractId";

import * as S from "./AppLayout.styled";

const MIN_KEYWORD_LENGTH = 2;
Expand All @@ -13,9 +15,7 @@ const AppLayout = () => {
const pathName = location.pathname;

const encodedKeyword =
location.pathname.split("/").length > MIN_KEYWORD_LENGTH
? location.pathname.split("/").pop()
: "";
location.pathname.split("/").length > MIN_KEYWORD_LENGTH ? extractLastPath(pathName) : "";
const receivedKeyword = encodedKeyword ? decodeURIComponent(encodedKeyword) : "";

const getHeader = (pathName: string) => {
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/components/pages/main/MainPage.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const MainPageContentContainer = styled.div`
margin-top: ${SPACING.m};
padding: ${SPACING.m};
min-height: calc(100vh - 7.6rem);
`;

export const MainPageHeaderContainer = styled.div`
Expand All @@ -19,9 +20,16 @@ export const MainPageHeaderContainer = styled.div`
gap: ${SPACING.s};
`;

export const SearchFallbackWrapper = styled.div`
flex: 1;
position: relative;
`;

export const MainPageTraveloguesList = styled.ul`
display: flex;
flex: 1;
flex-direction: column;
justify-content: center;
gap: ${SPACING.m};
`;
Expand Down
45 changes: 27 additions & 18 deletions frontend/src/components/pages/main/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { css } from "@emotion/react";

import useInfiniteTravelogues from "@queries/useInfiniteTravelogues";

import { Chip, Text } from "@components/common";
import FloatingButton from "@components/common/FloatingButton/FloatingButton";
import { Chip, FloatingButton, SearchFallback, Text } from "@components/common";
import TravelogueCard from "@components/pages/main/TravelogueCard/TravelogueCard";

import { useDragScroll } from "@hooks/useDragScroll";
import useIntersectionObserver from "@hooks/useIntersectionObserver";
import useTagSelection from "@hooks/useTagSelection";

import { FORM_VALIDATIONS_MAP } from "@constants/formValidation";

import * as S from "./MainPage.styled";
import TravelogueCardSkeleton from "./TravelogueCard/skeleton/TravelogueCardSkeleton";

Expand All @@ -25,12 +26,14 @@ const MainPage = () => {

const { lastElementRef } = useIntersectionObserver(fetchNextPage);

const hasTravelogue = travelogues.length > 0;

return (
<S.MainPageContentContainer>
<S.MainPageHeaderContainer>
<Text textType="title">지금 뜨고 있는 여행기</Text>
<Text textType="detail" css={S.subTitleColorStyle}>
다른 이들의 여행을 한 번 구경해보세요.
{`다른 이들의 여행을 한 번 구경해보세요. (태그는 최대 ${FORM_VALIDATIONS_MAP.tags.maxCount}개까지 가능해요!)`}
</Text>
</S.MainPageHeaderContainer>

Expand Down Expand Up @@ -58,21 +61,27 @@ const MainPage = () => {
</S.MainPageTraveloguesList>
)}
<S.MainPageTraveloguesList>
{travelogues.map(
({ authorProfileUrl, authorNickname, id, title, thumbnail, likeCount, tags }) => (
<TravelogueCard
key={id}
travelogueOverview={{
authorProfileUrl,
id,
title,
thumbnail,
likeCount,
authorNickname,
tags,
}}
/>
),
{hasTravelogue ? (
travelogues.map(
({ authorProfileUrl, authorNickname, id, title, thumbnail, likeCount, tags }) => (
<TravelogueCard
key={id}
travelogueOverview={{
authorProfileUrl,
id,
title,
thumbnail,
likeCount,
authorNickname,
tags,
}}
/>
),
)
) : (
<S.SearchFallbackWrapper>
<SearchFallback title="휑" text="다른 태그를 선택해 주세요!" />
</S.SearchFallbackWrapper>
)}
</S.MainPageTraveloguesList>
<FloatingButton />
Expand Down
21 changes: 11 additions & 10 deletions frontend/src/components/pages/search/SearchPage.styled.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import styled from "@emotion/styled";

import { SPACING } from "@styles/tokens";

export const Layout = styled.div`
display: flex;
flex-direction: column;
padding: 1.6rem;
gap: ${SPACING.m};
gap: ${({ theme }) => theme.spacing.s};
margin-top: ${SPACING.m};
min-height: calc(100vh - 6rem);
padding: ${SPACING.m};
`;

h1 {
${({ theme }) => theme.typography.mobile.title};
}
export const SearchFallbackWrapper = styled.div`
flex: 1;
position: relative;
`;

export const MainPageTraveloguesList = styled.ul`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing.m};
`;

export const MainPageContentContainer = styled.div`
padding-top: 1.6rem;
gap: ${SPACING.m};
`;
Loading

0 comments on commit 5d28555

Please sign in to comment.