[251210] feat: FeaturedBrandsPanel-TopBannerImage Popup
🕐 커밋 시간: 2025. 12. 10. 16:33:37 📊 변경 통계: • 총 파일: 6개 • 추가: +69줄 • 삭제: -5줄 📁 추가된 파일: + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerPopup.figma.jsx + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerPopup.jsx + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerPopup.module.less 📝 수정된 파일: ~ com.twin.app.shoptime/src/components/GlobalPopup/GlobalPopup.jsx ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/Banner/Banner.jsx ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.jsx 🔧 주요 변경 내용: • UI 컴포넌트 아키텍처 개선 • 소규모 기능 개선 • 모듈 구조 개선
This commit is contained in:
@@ -18,6 +18,7 @@ import { setHidePopup } from '../../actions/commonActions';
|
|||||||
import { getPopupConfig } from '../../constants/popupConfig';
|
import { getPopupConfig } from '../../constants/popupConfig';
|
||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import TPopUp from '../TPopUp/TPopUp';
|
import TPopUp from '../TPopUp/TPopUp';
|
||||||
|
import TopBannerPopup from '../../views/FeaturedBrandsPanel/TopBannerImage/TopBannerPopup';
|
||||||
|
|
||||||
// 커스텀 훅: 팝업 상태 관리
|
// 커스텀 훅: 팝업 상태 관리
|
||||||
const useGlobalPopupState = () => {
|
const useGlobalPopupState = () => {
|
||||||
@@ -128,9 +129,16 @@ const GlobalPopup = () => {
|
|||||||
secondaryData
|
secondaryData
|
||||||
} = useGlobalPopupState();
|
} = useGlobalPopupState();
|
||||||
|
|
||||||
|
const [imageDimensions, setImageDimensions] = React.useState({ width: 0, height: 0 });
|
||||||
|
|
||||||
const handlers = usePopupCloseHandlers();
|
const handlers = usePopupCloseHandlers();
|
||||||
const previousPopupVisible = usePrevious(popupVisible);
|
const previousPopupVisible = usePrevious(popupVisible);
|
||||||
|
|
||||||
|
const handleImageLoad = useCallback((dimensions) => {
|
||||||
|
console.log("[GLOBAL-POPUP] Image dimensions received:", dimensions);
|
||||||
|
setImageDimensions(dimensions);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 현재 팝업 설정
|
// 현재 팝업 설정
|
||||||
const currentConfig = useMemo(() => {
|
const currentConfig = useMemo(() => {
|
||||||
if (!activePopup) return null;
|
if (!activePopup) return null;
|
||||||
@@ -214,6 +222,47 @@ const GlobalPopup = () => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TopBannerImagePopup 특수 처리
|
||||||
|
if (activePopup === 'topBannerImagePopup') {
|
||||||
|
// Figma 디자인 기반 고정 크기
|
||||||
|
// 너비: 1060px
|
||||||
|
// 높이: 헤더(110px) + 이미지(556px) + 푸터(138px) = 804px
|
||||||
|
const popupWidth = '1060px';
|
||||||
|
const popupHeight = '804px';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
zIndex: 9999
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
width: popupWidth,
|
||||||
|
height: popupHeight,
|
||||||
|
backgroundColor: 'white',
|
||||||
|
borderRadius: '12px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column'
|
||||||
|
}}>
|
||||||
|
<TopBannerPopup
|
||||||
|
title={popupData?.pupBanrImgNm || 'Popup'}
|
||||||
|
imageUrl={popupData?.pupBanrImgUrl}
|
||||||
|
imageAlt={popupData?.pupBanrImgNm || 'Popup Banner'}
|
||||||
|
onImageLoad={handleImageLoad}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 설정이 없으면 기본 팝업도 렌더링하지 않음
|
// 설정이 없으면 기본 팝업도 렌더링하지 않음
|
||||||
if (!currentConfig) {
|
if (!currentConfig) {
|
||||||
console.warn(`No configuration found for popup type: ${activePopup}`);
|
console.warn(`No configuration found for popup type: ${activePopup}`);
|
||||||
|
|||||||
@@ -26,11 +26,12 @@ export default memo(function Banner({
|
|||||||
console.log("[FB-BANNER-COMP] isNBCU:", isNBCU);
|
console.log("[FB-BANNER-COMP] isNBCU:", isNBCU);
|
||||||
console.log("[FB-BANNER-COMP] brandTopBannerInfo:", brandTopBannerInfo);
|
console.log("[FB-BANNER-COMP] brandTopBannerInfo:", brandTopBannerInfo);
|
||||||
|
|
||||||
// Top Banner 정보에서 필요한 필드 추출 (현재는 사용하지 않음)
|
// Top Banner 정보에서 필요한 필드 추출
|
||||||
const {
|
const {
|
||||||
banrImgUrl, // 배너 이미지 URL
|
banrImgUrl, // 배너 이미지 URL
|
||||||
banrImgNm, // 배너 이미지 이름
|
banrImgNm, // 배너 이미지 이름
|
||||||
pupBanrImgUrl, // 팝업 배너 이미지 URL
|
pupBanrImgUrl, // 팝업 배너 이미지 URL
|
||||||
|
pupBanrImgNm, // 팝업 배너 이미지 이름
|
||||||
pupBanrTtl, // 팝업 배너 타이틀
|
pupBanrTtl, // 팝업 배너 타이틀
|
||||||
banrNm // 배너 이름
|
banrNm // 배너 이름
|
||||||
} = brandTopBannerInfo || {};
|
} = brandTopBannerInfo || {};
|
||||||
@@ -76,6 +77,8 @@ export default memo(function Banner({
|
|||||||
banrImgUrl={banrImgUrl}
|
banrImgUrl={banrImgUrl}
|
||||||
banrImgNm={banrImgNm}
|
banrImgNm={banrImgNm}
|
||||||
banrNm={banrNm}
|
banrNm={banrNm}
|
||||||
|
pupBanrImgUrl={pupBanrImgUrl}
|
||||||
|
pupBanrImgNm={pupBanrImgNm}
|
||||||
spotlightId="nbcu-top-banner-image"
|
spotlightId="nbcu-top-banner-image"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import React, { memo, useCallback, useState } from "react";
|
import React, { memo, useCallback, useState } from "react";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { setShowPopup } from "../../../actions/commonActions";
|
||||||
import CustomImage from "../../../components/CustomImage/CustomImage";
|
import CustomImage from "../../../components/CustomImage/CustomImage";
|
||||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
import css from "./TopBannerImage.module.less";
|
import css from "./TopBannerImage.module.less";
|
||||||
@@ -8,16 +10,26 @@ const Container = SpotlightContainerDecorator(
|
|||||||
"div"
|
"div"
|
||||||
);
|
);
|
||||||
|
|
||||||
const TopBannerImage = memo(({ banrImgUrl, banrImgNm, banrNm, spotlightId }) => {
|
const TopBannerImage = memo(({ banrImgUrl, banrImgNm, banrNm, pupBanrImgUrl, pupBanrImgNm, spotlightId }) => {
|
||||||
console.log("[TOP-BANNER-IMG] Rendering with URL:", banrImgUrl);
|
console.log("[TOP-BANNER-IMG] Rendering with URL:", banrImgUrl);
|
||||||
console.log("[TOP-BANNER-IMG] spotlightId:", spotlightId);
|
console.log("[TOP-BANNER-IMG] spotlightId:", spotlightId);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 });
|
const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 });
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
console.log("[TOP-BANNER-IMG] Clicked");
|
console.log("[TOP-BANNER-IMG] Clicked - Opening popup");
|
||||||
// 필요시 클릭 핸들러 로직 추가
|
if (pupBanrImgUrl) {
|
||||||
}, []);
|
console.log("[TOP-BANNER-IMG] Dispatching topBannerImagePopup");
|
||||||
|
dispatch(setShowPopup({
|
||||||
|
activePopup: 'topBannerImagePopup',
|
||||||
|
data: {
|
||||||
|
pupBanrImgUrl,
|
||||||
|
pupBanrImgNm: pupBanrImgNm || banrImgNm || banrNm
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [dispatch, pupBanrImgUrl, pupBanrImgNm, banrImgNm, banrNm]);
|
||||||
|
|
||||||
const handleImageLoad = useCallback((e) => {
|
const handleImageLoad = useCallback((e) => {
|
||||||
const img = e.target;
|
const img = e.target;
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<div style={{width: '100%', height: '100%', background: 'white', overflow: 'hidden', borderRadius: 12, flexDirection: 'column', justifyContent: 'center', alignItems: 'center', display: 'inline-flex'}}>
|
||||||
|
<div style={{alignSelf: 'stretch', height: 119, padding: 30, background: '#E7EBEF', justifyContent: 'flex-start', alignItems: 'center', gap: 15, display: 'inline-flex'}}>
|
||||||
|
<div style={{textAlign: 'center', color: 'black', fontSize: 42, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 42, wordWrap: 'break-word'}}>Wells Fargo Active Cash Credit Card</div>
|
||||||
|
</div>
|
||||||
|
<div style={{alignSelf: 'stretch', justifyContent: 'center', alignItems: 'center', display: 'inline-flex'}}>
|
||||||
|
<img style={{flex: '1 1 0', height: 555.51}} src="https://placehold.co/1060x556" />
|
||||||
|
</div>
|
||||||
|
<div style={{alignSelf: 'stretch', paddingLeft: 60, paddingRight: 60, paddingTop: 30, paddingBottom: 30, justifyContent: 'center', alignItems: 'center', gap: 10, display: 'inline-flex'}}>
|
||||||
|
<div style={{width: 300, height: 78, background: '#7A808D', borderRadius: 12, justifyContent: 'center', alignItems: 'center', gap: 10, display: 'flex'}}>
|
||||||
|
<div style={{textAlign: 'center', color: 'white', fontSize: 30, fontFamily: 'LG Smart UI', fontWeight: '700', lineHeight: 30, wordWrap: 'break-word'}}>CLOSE</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import React, { memo, useCallback, useState } from "react";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { setHidePopup } from "../../../actions/commonActions";
|
||||||
|
import css from "./TopBannerPopup.module.less";
|
||||||
|
|
||||||
|
const TopBannerPopup = memo(({ title, imageUrl, imageAlt, onImageLoad }) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 });
|
||||||
|
|
||||||
|
const handleImageLoad = useCallback((e) => {
|
||||||
|
const img = e.target;
|
||||||
|
console.log("[TOP-BANNER-POPUP] Image loaded - dimensions:", img.naturalWidth, "x", img.naturalHeight);
|
||||||
|
|
||||||
|
const dimensions = {
|
||||||
|
width: img.naturalWidth,
|
||||||
|
height: img.naturalHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
setImageDimensions(dimensions);
|
||||||
|
|
||||||
|
// 부모 컴포넌트에 크기 전달
|
||||||
|
if (onImageLoad) {
|
||||||
|
onImageLoad(dimensions);
|
||||||
|
}
|
||||||
|
}, [onImageLoad]);
|
||||||
|
|
||||||
|
const handleClose = useCallback(() => {
|
||||||
|
console.log("[TOP-BANNER-POPUP] Closing popup");
|
||||||
|
dispatch(setHidePopup());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={css.container}>
|
||||||
|
{/* Title Section */}
|
||||||
|
<div className={css.titleSection}>
|
||||||
|
<div className={css.titleText}>{title}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Image Section */}
|
||||||
|
<div className={css.imageSection}>
|
||||||
|
<img
|
||||||
|
src={imageUrl}
|
||||||
|
alt={imageAlt || "Popup Banner"}
|
||||||
|
className={css.popupImage}
|
||||||
|
onLoad={handleImageLoad}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Button Section */}
|
||||||
|
<div className={css.buttonSection}>
|
||||||
|
<button className={css.closeButton} onClick={handleClose} aria-label="Close popup">
|
||||||
|
CLOSE
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
TopBannerPopup.displayName = "TopBannerPopup";
|
||||||
|
|
||||||
|
export default TopBannerPopup;
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: white;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 12px;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 헤더: 높이 110px (상단 마진 30 + 내용 50 + 하단 마진 30)
|
||||||
|
.titleSection {
|
||||||
|
flex: 0 0 110px;
|
||||||
|
background: #E7EBEF;
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
padding-left: 30px;
|
||||||
|
padding-right: 30px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleText {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
color: black;
|
||||||
|
font-size: 42px;
|
||||||
|
font-family: 'LG Smart UI', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 42px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 이미지: 높이 556px
|
||||||
|
.imageSection {
|
||||||
|
flex: 0 0 556px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
background: white;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popupImage {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 푸터: 높이 138px (상단 마진 30 + 버튼 78 + 하단 마진 30)
|
||||||
|
.buttonSection {
|
||||||
|
flex: 0 0 138px;
|
||||||
|
padding-left: 60px;
|
||||||
|
padding-right: 60px;
|
||||||
|
padding-top: 30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.closeButton {
|
||||||
|
width: 300px;
|
||||||
|
height: 78px;
|
||||||
|
background: #7A808D;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
font-size: 30px;
|
||||||
|
font-family: 'LG Smart UI', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #6a7278;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid #fff;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #5a6268;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user