-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Team23] 캐로셀 적용, 모달창 설계 및 구현, 상품 이미지 호버 적용 (#30)
* [Add] Carousel - 캐러셀 구현 - 캐러셀 틀을 잡기 위해 구조 살짝 변경 - 컴포넌트 트리 참고 - products.length가 8의 배수가 아닌 경우를 생각해서 로직을 짰는데 아직 테스트는 안해봄 * [Fix] Carousel - 4의 배수가 아닐 때 캐러셀 애니메이션 테스트함 - left 버튼을 누를 때 문제가 있었는데 index 업데이트를 수정하여 고침 * [Fix] Direction of carousel-button - 왼쪽 버튼을 누르면 이전 슬라이드가, 오른쪽 버튼을 누르면 이후 슬라이드가 나오도록 수정 * [Add] hover ver.1 - 리렌더링 때문에 깜빡거림 * [Feat] 카드 이미지에 hover 시 배송 타입 노출 * [Add] Modal skeleton code * [Refactor] Modal 컴포넌트 분리 - 공통적으로 사용되는 styled component를 common.jsx에 분리 - 카드에 있는 정보를 모달에 전달 * [Fix] 할인 전후 가격 잘못 넣어서 수정 * [Add] Modal에 클릭한 상품의 데이터를 넣어줌 Co-authored-by: Hongbi <dyongdi@gmail.com>
- Loading branch information
1 parent
631fc3a
commit 5b1790b
Showing
13 changed files
with
581 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
) | ||
.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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
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); | ||
}; | ||
|
||
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) => { | ||
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.