Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Team23] 캐로셀 적용, 모달창 설계 및 구현, 상품 이미지 호버 적용 #30

Merged
merged 10 commits into from
Apr 26, 2021
6 changes: 3 additions & 3 deletions banchan/src/components/StateProvider.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from "react";
// import { useState } from "react";
import Header from "./header/Header";
import MainPage from "./main/MainPage";
import Test from "./Test";
// import Test from "./Test";

const StateProvider = () => {
// const [loginState, setLoginState] = useState(false);
Expand All @@ -11,7 +11,7 @@ const StateProvider = () => {
<>
<Header />
<MainPage />
<Test />
{/* <Test /> */}
</>
);
};
Expand Down
36 changes: 36 additions & 0 deletions banchan/src/components/main/CarouselList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useEffect, useState } from 'react';
import styled from 'styled-components';
import Card from '../utils/Card';
import theme from '../utils/styles/theme';

const CarouselList = (props) => {
const [products, setProducts] = useState([]);

useEffect(() => {
fetch(
'https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/main'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

자주 쓰는 API를 상수로 지정하거나 객체 형태로 모아서 쓰는 건 어떨까요 ?

)
.then((response) => response.json())
.then((result) => setProducts(result.body))
.then((error) => console.log('error', error));
}, []);

return (
<StyledUl>
{products.map((product) => (
<Card
key={product.detail_hash}
product={product}
cardSize={theme.cardSizes.M}
/>
))}
</StyledUl>
);
};

const StyledUl = styled.ul`
display: flex;
justify-content: space-evenly;
`;

export default CarouselList;
142 changes: 123 additions & 19 deletions banchan/src/components/main/CarouselSection.jsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,134 @@
import { useEffect, useState } from "react";
import Card from "../utils/Card";
import theme from "../utils/styles/theme";
import { useEffect, useRef, useState } from 'react';
// import { mockData, temp } from "../utils/mockData.js";
import Card from '../utils/Card';
import styled from 'styled-components';
import IconButton from '../utils/button/IconButton';
import { CenterContainer } from '../utils/styles/common';

const CarouselSection = (props) => {
const CarouselSection = ({ key, url, title, onModal }) => {
const [products, setProducts] = useState([]);
// const [products, setProducts] = useState(mockData);
const [currentX, setX] = useState(0);
const [currentIndex, setCurrentIndex] = useState(4);
const [rightDisabled, setLeftDisabled] = useState(false);
const [leftDisabled, setRightDisabled] = useState(true);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left right disabled를 state로 갖지 않고, 슬라이드의 Index와 length로 충분히 처리할 수 있을 것 같습니다!
이런 방향성도 고려해보세요!

const slides = useRef();
const slideCount = 4;
const slideWidth = 320;
const totalWidth = 320 * slideCount + 16;

useEffect(() => {
fetch(
"https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/main"
)
.then((response) => response.json())
.then((result) => setProducts(result.body))
.then((error) => console.log("error", error));
(async () => {
const response = await fetch(url);
const result = await response.json();
setProducts(result.body);
})();
}, []);

const moveSlide = (speed, distance, nextIndex) => {
slides.current.style.transition = `${speed}ms`;
slides.current.style.transform = `translateX(${distance}px)`;
setX(distance);
setCurrentIndex(nextIndex);
};

const moveRight = () => {
const remainSlideCount = products.length - currentIndex;
const distance =
remainSlideCount >= slideCount
? currentX - totalWidth
: currentX - slideWidth * remainSlideCount;
const nextIndex =
remainSlideCount >= slideCount
? currentIndex + slideCount
: currentIndex + remainSlideCount;
moveSlide(300, distance, nextIndex);
distance && setRightDisabled(false);
nextIndex >= products.length && setLeftDisabled(true);
};

const moveLeft = () => {
const remainSlideCount = currentIndex - slideCount;
const distance =
remainSlideCount >= slideCount
? currentX + totalWidth
: currentX + slideWidth * remainSlideCount;
const nextIndex =
remainSlideCount >= slideCount
? currentIndex - slideCount
: currentIndex - remainSlideCount;
moveSlide(300, distance, nextIndex);
!distance && setRightDisabled(true);
nextIndex < products.length && setLeftDisabled(false);
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

동일한 로직에 좌우로만 차이가 있는데 이를 통합한 하나의 함수로 짜보는 건 어떨까요 ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵! 수정하겠습니다

return (
<ul>
{products.map((product) => (
<Card
key={product.detail_hash}
product={product}
cardSize={theme.cardSizes.M}
/>
))}
</ul>
<SectionContainer>
<SectionBox>
<SectionTitle>{title}</SectionTitle>
<SectionContent>
<CardList ref={slides}>
{products.map((product) => (
<Card
key={product.detail_hash}
product={product}
cardSize={(props) => props.theme.cardSizes.M}
margin={8}
onModal={onModal}
/>
))}
</CardList>
</SectionContent>
</SectionBox>
<SectionButton>
<IconButton type="LEFT" fn={moveLeft} disabled={leftDisabled} />
<IconButton type="RIGHT" fn={moveRight} disabled={rightDisabled} />
</SectionButton>
</SectionContainer>
);
};

const SectionContainer = styled(CenterContainer)`
position: relative;
margin: 30px 0;
/*border: 1px solid blue;*/
width: 1320px;
`;

const SectionBox = styled.div`
/*border: 1px solid violet;*/
`;

const SectionContent = styled.div`
position: relative;
display: flex;
margin: 20px 0;
width: 1296px; /* 원래 1280 */
height: 500px;
/*border: 1px solid black;*/
overflow: hidden;
`;

const CardList = styled(CenterContainer)`
list-style: none;
align-items: start;
position: absolute;
`;

const SectionTitle = styled.div`
font-size: ${(props) => props.theme.fontSizes.XL};
font-weight: bold;
color: ${(props) => props.theme.darkGray};
margin: 20px 0;
`;

const SectionButton = styled.div`
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
display: flex;
justify-content: space-between;
`;

export default CarouselSection;
33 changes: 32 additions & 1 deletion banchan/src/components/main/CarouselSectionList.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
const CarouselSectionList = (props) => <></>;
import { useState } from 'react';
import { URLS } from '../utils/variables.js';
import CarouselSection from './CarouselSection.jsx';
import styled from 'styled-components';
import { CenterContainer } from '../utils/styles/common.jsx';

const CarouselSectionList = (props) => {
const [sections, setSections] = useState([
{ id: 0, kind: 'main', title: '모두가 좋아하는 든든한 메인요리' },
{ id: 1, kind: 'soup', title: '정성이 담긴 뜨끈한 국물요리' },
{ id: 2, kind: 'side', title: '식탁을 풍성하게 하는 정갈한 밑반찬' },
]);

return (
<CarouselContainer>
{sections.map((section) => (
<CarouselSection
key={section.id}
url={URLS.base.concat(section.kind)}
title={section.title}
onModal={props.onModal}
/>
))}
</CarouselContainer>
);
};

const CarouselContainer = styled(CenterContainer)`
/*border: 1px solid red;*/
display: flex;
flex-direction: column;
`;

export default CarouselSectionList;
57 changes: 53 additions & 4 deletions banchan/src/components/main/MainPage.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,62 @@
import CarouselSection from "./CarouselSection";
import TabSection from "./tab/TabSection";
import TabSection from './tab/TabSection';
import CarouselSectionList from './CarouselSectionList';
import { useEffect, useState } from 'react';
import styled from 'styled-components';
import { CenterContainer } from '../utils/styles/common';
import Modal from './Modal';

const MainPage = (props) => {
const [modalState, setModalState] = useState(false);
const [modalData, setModalData] = useState({});
const [detailDataMap, setDetailDataMap] = useState(new Map());

useEffect(() => {
fetch(
'https://h3rb9c0ugl.execute-api.ap-northeast-2.amazonaws.com/develop/baminchan/detail'
)
.then((res) => res.json())
.then((response) => {
response.body.forEach((e) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e같은 추상적인 변수명보다는 구체적인 변수명이 좋습니다!

setDetailDataMap(detailDataMap.set(e.hash, e.data));
});
});
}, []);

const handleModal = (product) => {
setModalState(true);
const detailData = detailDataMap.get(product.detail_hash);
setModalData({ ...product, ...detailData });
};

return (
<>
<TabSection />
<CarouselSection />
<TabSection onModal={handleModal} />
<CarouselSectionList onModal={handleModal} />
{modalState && (
<ModalBackground>
<ModalContainer>
<Modal product={modalData} />
<button onClick={() => setModalState(false)}>X</button>
</ModalContainer>
</ModalBackground>
)}
</>
);
};

const ModalBackground = styled(CenterContainer)`
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.4);
width: 100%;
height: 100%;
z-index: 5;
`;

const ModalContainer = styled.div`
display: flex;
align-items: flex-start;
`;

export default MainPage;
77 changes: 77 additions & 0 deletions banchan/src/components/main/Modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import styled from 'styled-components';
import Price from '../utils/Price';
import TextButton from '../utils/button/TextButton';
import {
LabelList,
StyledDescription,
StyledTitle,
} from '../utils/styles/common';

const Modal = ({ product }) => {
console.log(product);
return (
<ModalCard>
<ProductImage>
<img src={product.top_image} alt="product-thumbnail" />
<ThumbnailUL>
{product.thumb_images.map((i) => (
<li>
<Thumbnail src={i} />
</li>
))}
</ThumbnailUL>
</ProductImage>
<Information>
<ProductMainInfo>
<StyledTitle>{product.title}</StyledTitle>
<StyledDescription>{product.description}</StyledDescription>
<div>
<LabelList />
<Price product={product} />
</div>
</ProductMainInfo>
<ProductBuyInfo>
<div>적립금: {product.point}</div>
<div>배송정보: {product.delivery_info}</div>
<div>배송비 : {product.delivery_fee}</div>
</ProductBuyInfo>
<ProductCount></ProductCount>
<ProductPrice>
여기는 총 주문 금액이 들어갈 예정입니다. 카운트를 같이 계산해서..
</ProductPrice>
<TextButton type="ORDER"></TextButton>
</Information>
</ModalCard>
);
};

const ModalCard = styled.div`
background: white;
width: 960px;
height: 1076px;
`;

const ThumbnailUL = styled.div`
display: flex;
`;

const Thumbnail = styled.img`
width: 100px;
height: 100px;
`;

const ProductImage = styled.div``;

const Information = styled.div``;

const ProductMainInfo = styled.div``;

// const LabelList = styled.div``;

const ProductBuyInfo = styled.div``;

const ProductCount = styled.div``;

const ProductPrice = styled.div``;

export default Modal;
Loading