728x90
퍼블리싱으로 제공받은 CSS를 사용하여 리액트에서 하트를 누르면 단골상점으로 등록하는 작업을 진행했다.
CSS
하트와 관련된 스타일이 8개가 있었다.
/* 하트 지정 시 필요한 스타일 */
/* 1. 하트 옆 체크박스 숨기는 */
.like-check-box input[type=checkbox] {
position: absolute;
width: 0px;
height: 0px;
overflow: hidden;
}
/* 2. 하트 박스 전체가 아예 노출이 안되게 하는 스타일 */
.like-check-box input[type=checkbox] + label {
position: relative;
display: inline-block;
border: 0.0625rem solid #e0e0e0;
border-radius: 0.25rem;
padding: 0.375rem 0.6875rem;
padding-left: 1.1875rem;
font-size: 0.6875rem;
letter-spacing: -0.05em;
color: #999;
background-color: #ffffff;
}
/* 3. [하트 모양] - 클릭 전 회색*/
.like-check-box input[type=checkbox] + label:after {
content: "";
width: 0.5rem;
height: 0.5rem;
background-image: url(../img/ico_heart_gray.png);
background-position: cetner;
background-repeat: no-repeat;
background-size: cover;
position: absolute;
left: 0.5625rem;
top: 0.4375rem;
}
/* 4. [하트 테두리] - 클릭 후 빨간색 */
.like-check-box input[type=checkbox]:checked + label {
border: 0.0625rem solid #ff7f00;
background-color: #fff6ed;
color: #ff7f00;
}
/* 5. [하트 모양] - 클릭 후 빨간색 */
.like-check-box input[type=checkbox]:checked + label:after {
background-image: url(../img/ico_heart.png);
}
/* 6. [하트 테두리] 텍스트 없을 때 사용 - 찌그러진 하트 테두리기 나옴 (이유는 모르겠다)*/
.like-check-box.none-text input[type=checkbox] + label {
width: 1rem;
height: 1rem;
padding: 0;
}
/* 7. [하트 테두리] 텍스트 없을 때 사용 - 하트가 밖으로 빠져나감 (이유는 모르겠다)*/
.like-check-box.none-text input[type=checkbox] + label:after {
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
/* 8. 하트 관련 모든 속성을 빨간 색으로 변경 (현재는 없어도 동작함)*/
.like-check-box .total {
color: #e82f01 !important;
}
최종 TSX 페이지
/* eslint-disable @next/next/no-img-element */
import React, { useState, useRef, useEffect } from "react";
import { useRouter } from "next/router";
import Layout from "@comps/Layout";
import Main from "@comps/Main";
import { putRegularMap } from "@apis/member";
import Toast from "@comps/toast";
import {
Label,
Input,
Button,
WindmillContext,
} from "@roketid/windmill-react-ui";
function RecordPage() {
/**
|--------------------------------------------------
| 😃 1. 일반 변수
|--------------------------------------------------
*/
const router = useRouter();
const { idx } = router.query;
const [data, setData] = useState<any>();
const [isRegular, setIsRegular] = useState<boolean>(false); // 단골 유무
const [isToast, setIsToast] = useState<boolean>(false); // Toast 메시지 팝업
/**
|--------------------------------------------------
| 🔑 2. 일반 함수
|--------------------------------------------------
*/
/** 하트 클릭 시 */
const handlePutRegularMap = () => {
setIsRegular(!isRegular);
setIsToast(true)
fetchPutRegularMap();
// setIsToast(false)
}
/**
|--------------------------------------------------
| 🎁 3. fetch 함수
|--------------------------------------------------
*/
/**3-1. 상단의 상점 정보 조회 */
async function fetchData(idx: any) {
const request = {
idx: Number(idx),
};
const response = await getDetailReserve(request);
if (response.status == 200) {
setData(response.data);
// 날짜 쪼개기
const reserveDate = response.data.reserveDate;
let [datePart, timePart] = reserveDate.split(' ');
datePart = datePart.replace(/-/g, '.');
timePart = timePart.replace(/:00$/, '');
setDate(datePart)
setTime(timePart)
}
}
/** 단골 등록 */
async function fetchPutRegularMap() {
const request = {
storeIdx: data.storeIdx
};
const response = await putRegularMap(request);
if (response.status == 200) {
fetchData(idx)
console.log('좋아요 성공!!!')
} else {
alert('실패... ')
}
}
/**
|--------------------------------------------------
| ⚡ 5. useEffect
|--------------------------------------------------
*/
/** 목록 조회 GET */
useEffect(() => {
fetchData(idx);
}, [idx]); // idx가 변경될 때마다 fetchData() 호출
/*
|--------------------------------------------------
| 🎨 화면
|--------------------------------------------------
*/
return (
<>
<Layout />
<Main>
<body>
<div className="header">
<div className="top-bar">
<div className="prev">
<a href="" className="prev-btn">
<i className="icon icon-prev"></i>
예약이력
</a>
</div>
</div>
</div>
<div className="section">
<div className="wrapper">
<ul className="reservation-list">
<li>
<div className="box">
<div className="reservation">
<a href="#">
<div className="branch-info">
<div className="prefix">
<img src="/img/sample-img-banner.png" alt="" />
<div className="sucess">
<i className="icon ico-calendar"></i>
예약 완료
</div>
</div>
<div className="suffix">
<div className="address">
레저로 스크린골프 계양점
<div className="sub-address gray">
인천광역시 계약구 계산대로 88
</div>
</div>
<div className="tag-check-wrap">
{/* 🎯 하트 적용 부분 */}
<div className="like-check-box none-text">
<input type="checkbox" name="" id="ck" checked={isRegular} onChange={handlePutRegularMap} />
<label htmlFor="ck"></label>
</div>
</div>
</div>
</div>
</a>
</div>
</div>
</li>
</ul>
</div>
</div>
{(isRegular && isToast) ? (
<Toast message="단골 매장으로 설정했습니다." setToast={setIsToast} />
) : (
!isRegular && isToast && (
<Toast message="단골 매장을 해제했습니다." setToast={setIsToast} />
)
)}
</body>
</Main>
</>
);
}
export default RecordPage;
핵심 로직은 아래와 같다.
{/* 🎯 하트 적용 부분 */}
<div className="like-check-box none-text">
<input type="checkbox" name="" id="ck" checked={isRegular} onChange={handlePutRegularMap} />
<label htmlFor="ck"></label>
</div>
완료!
'Frond-end > React' 카테고리의 다른 글
[React] 엄청 간단한 카카오 로그인 구현 (0) | 2024.03.06 |
---|---|
[React] 글과 이미지가 같이 있는 글 저장해서 보여주는 방법 (0) | 2024.03.02 |
[React] 리액트에서 텍스트 줄바꿈(개행)하는 방법 (whiteSpace: "pre-wrap") (0) | 2024.02.26 |
[React] datepicker 적용해보기 (0) | 2024.02.21 |
[Next.js] 게시판 화면, 상세조회, 팝업 컴포넌트 개발 (2) | 2024.02.15 |