Files
shoptime/com.twin.app.shoptime/src/components/TPopUp/TPopUp.jsx
2025-06-13 15:41:51 +09:00

384 lines
9.7 KiB
JavaScript

import React, { useCallback, useEffect, useMemo, useState } from "react";
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import Alert from "@enact/sandstone/Alert";
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import defaultImageItem from "../../../assets/images/img-thumb-empty-product@3x.png";
import { cancelFocusElement, focusElement } from "../../actions/commonActions";
import { SpotlightIds } from "../../utils/SpotlightIds";
import CustomImage from "../CustomImage/CustomImage";
import TButton from "../TButton/TButton";
import css from "../TPopUp/TPopUp.module.less";
import { $L } from "../../utils/helperMethods";
const Container = SpotlightContainerDecorator(
{ enterTo: "default-element" },
Spottable("div")
);
const OptionContainer = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
Spottable("ul")
);
const ButtonContainer = SpotlightContainerDecorator("div");
const ButtonContainerNegative = SpotlightContainerDecorator(
{ defaultElement: `[data-spotlight-id="${"tPopupBtn2"}"]` },
"div"
);
const SpottableComponent = Spottable("li");
const SpottableDiv = Spottable("div");
const KINDS = [
"textPopup",
"termsPopup",
"introTermsPopup",
"optionPopup",
"eventBannerPopup",
"supportPopup",
"exitPopup",
"couponPopup",
"mobileSendPopup",
"qrPopup",
"checkoutTermsPopup",
"scrollPopup",
"watchPopup",
"setPinCodePopup",
"descriptionPopup",
"errorPopup",
"orderDetailPopup",
"returnExchangePopup",
"orderCancelPopup",
"cancelConfirmPopup",
"trackPackagePopup",
"optionalAgreement",
"normal",
];
export default function TPopUp({
kind,
children,
onExit,
onClick,
currentPage,
onRightClick,
onLeftClick,
itemLength,
onClose,
hasButton,
hasText,
hasOnClose = false,
hasIndicator,
hasLogo,
hasThumbnail,
thumbnail,
logo,
className,
button1Text,
button2Text,
open,
title,
text,
type = "overlay",
options,
optionClick,
selectedIndex,
spotlightId,
onSpotlightRight,
...rest
}) {
const dispatch = useDispatch();
const popupVisible = useSelector((state) => state.common.popup.popupVisible);
const httpHeader = useSelector((state) => state.common.httpHeader);
useEffect(() => {
if (popupVisible) {
if (spotlightId) {
const timerId = setTimeout(() => {
Spotlight.focus(spotlightId);
}, 0);
return () => {
clearTimeout(timerId);
};
}
const timerId = setTimeout(() => {
Spotlight.focus(SpotlightIds.TPOPUP);
}, 0);
return () => {
clearTimeout(timerId);
};
}
}, [spotlightId, popupVisible]);
useEffect(() => {
if (KINDS.indexOf(kind) < 0) {
console.error("TPopUp kind error");
}
}, [kind]);
const ButtonContainerComp = useMemo(() => {
return kind === "exitPopup" ? ButtonContainerNegative : ButtonContainer;
}, [kind]);
// optionalAgreement
// 자동으로 Yes/No 버튼 텍스트 설정
const finalButton1Text = useMemo(() => {
if (kind === "optionalAgreement" && !button1Text) {
return "Yes";
}
return button1Text;
}, [kind, button1Text]);
const finalButton2Text = useMemo(() => {
if (kind === "optionalAgreement" && !button2Text) {
return "No";
}
return button2Text;
}, [kind, button2Text]);
// optionalAgreement일 경우 항상 버튼 표시
const shouldShowButtons = useMemo(() => {
return hasButton || kind === "optionalAgreement";
}, [hasButton, kind]);
//-------------------------------------------------------
const _onClick = useCallback(
(e) => {
if (onClick) {
onClick(e);
} else if (kind === "exitPopup") _onExit();
else _onClose();
},
[onClick, kind, _onExit, _onClose]
);
const _optionClick = useCallback(
(e, idx) => {
if (optionClick) {
optionClick(e, idx);
}
},
[optionClick]
);
const _onExit = useCallback(
(e) => {
if (onExit) {
onExit(e);
}
},
[onExit]
);
const _onClose = useCallback(
(e) => {
if (onClose) {
onClose(e);
}
},
[onClose]
);
const selectedOptClick = useCallback(
(index, optionStockQuantity) => (e) => {
if (optionStockQuantity && optionStockQuantity <= 0) {
return;
}
_optionClick(index);
},
[_optionClick]
);
const _onLeftClick = useCallback(
(e) => {
if (onLeftClick) {
onLeftClick(e);
}
},
[onLeftClick]
);
const _onRightClick = useCallback(
(e) => {
if (onRightClick) {
onRightClick(e);
}
},
[onRightClick]
);
const _onSpotlightRight = useCallback(
(e) => {
if (onSpotlightRight) {
onSpotlightRight(e);
}
},
[onSpotlightRight]
);
const ariaHidden = useMemo(() => {
const deviceCountryCode = httpHeader?.["X-Device-Country"] || "";
if (deviceCountryCode === "US") {
return false;
} else {
return true;
}
}, [httpHeader]);
return (
<Alert
open={open}
onClose={_onClose}
type={type}
className={classNames(
css.tPopUp,
css[kind],
title && css.title,
text && css.text,
hasButton && css.hasButton,
kind === "optionalTermsConfirmPopup" && css.bottomPopup,
className ? className : null
)}
aria-hidden={ariaHidden}
>
<Container className={css.info} spotlightId={SpotlightIds.TPOPUP}>
{hasThumbnail && thumbnail && (
<div className={css.thumbnail}>
<CustomImage
src={thumbnail}
fallbackSrc={defaultImageItem}
alt="thumbnail"
/>
</div>
)}
{hasText && (
<div className={css.textLayer}>
{title && (
<div className={css.title} aria-label={title + "heading 1"}>
{title}
</div>
)}
{hasLogo && logo !== null && (
<span>
<CustomImage
src={logo}
fallbackSrc={defaultImageItem}
alt="logo"
/>
</span>
)}
{text && (
<div className={css.text} aria-label={text}>
{text}
</div>
)}
</div>
)}
{options && (
<OptionContainer
className={classNames(
css.optionLayer,
options.length >= 5 ? css.optionScroll : null
)}
>
{options.map((option, index) => {
const { prodOptCval, optImgUrl, optPrc, optStkQty, optNm } =
option;
const optStock = Number(optStkQty);
return (
<SpottableComponent
{...rest}
className={classNames(
css.option,
optStkQty > 0 &&
index === selectedIndex &&
css.selectedOption,
index == selectedIndex && css.focused,
optStkQty <= 0 && css.optionSoldOut
)}
spotlightId={`selectedOptionBtn-${index}`}
onClick={selectedOptClick(index, optStkQty)}
key={`option: ${index}`}
aria-label={optNm ? optNm : prodOptCval}
spotlightDisabled={optStkQty && optStkQty <= 0}
>
{optImgUrl && (
<CustomImage
src={optImgUrl}
className={css.img}
fallbackSrc={defaultImageItem}
alt="optionImg"
/>
)}
<div className={css.optionItem}>
<span>{optNm ? optNm : prodOptCval}</span>
{optStkQty && optStkQty <= 0 && (
<span>{$L("SOLD OUT")}</span>
)}
</div>
{optPrc && optStock !== 0 && `($${optPrc})`}
</SpottableComponent>
);
})}
</OptionContainer>
)}
{children}
{hasIndicator && (
<>
{currentPage !== 0 && (
<SpottableDiv
className={css.leftBtn}
onClick={_onLeftClick}
aria-label="Move to left"
role="button"
></SpottableDiv>
)}
{currentPage !== itemLength - 1 && (
<SpottableDiv
className={css.rightBtn}
onClick={_onRightClick}
aria-label="Move to Right"
role="button"
></SpottableDiv>
)}
</>
)}
{shouldShowButtons && (
<ButtonContainerComp className={css.buttonContainer}>
{finalButton1Text && (
<TButton
spotlightId="tPopupBtn1"
onClick={_onClick}
role="button"
ariaLabel={finalButton1Text}
onSpotlightRight={_onSpotlightRight}
>
{finalButton1Text}
</TButton>
)}
{finalButton2Text && (
<TButton
spotlightId="tPopupBtn2"
onClick={onClose}
role="button"
ariaLabel={finalButton2Text}
>
{finalButton2Text}
</TButton>
)}
</ButtonContainerComp>
)}
</Container>
</Alert>
);
}