[components] TItemCard, type 및 props 추가
Detail Notes : 1. vertical(thumbnail) type, horisontal type 추가 2. soldoutFlag props 추가 (“Y” 일경우 rendering) 3. rank 및 isBestSeller props 추가 4. onCardClick() return productId, props 추가
This commit is contained in:
@@ -1,68 +1,96 @@
|
|||||||
import React from "react";
|
import React, { memo, useCallback } from "react";
|
||||||
|
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
import Spottable from "@enact/spotlight/Spottable";
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
|
||||||
|
import { $L } from "../../utils/helperMethods";
|
||||||
import css from "./TItemCard.module.less";
|
import css from "./TItemCard.module.less";
|
||||||
|
|
||||||
const SpottableItemList = Spottable("li");
|
const SpottableComponent = Spottable("li");
|
||||||
|
|
||||||
export default function TItemCard({
|
const TYPE_VERTICAL = "vertical";
|
||||||
|
const TYPE_HORIZONTAL = "horizontal";
|
||||||
|
|
||||||
|
const SOLD_OUT_STRING = "SOLD OUT";
|
||||||
|
const TOP_STRING = "TOP";
|
||||||
|
|
||||||
|
export default memo(function ProductCard({
|
||||||
children,
|
children,
|
||||||
imageSource,
|
|
||||||
imageAlt,
|
imageAlt,
|
||||||
productName,
|
imageSource,
|
||||||
|
isBestSeller = false,
|
||||||
|
onCardClick,
|
||||||
priceInfo,
|
priceInfo,
|
||||||
rankOrd,
|
productId,
|
||||||
|
productName,
|
||||||
|
rank,
|
||||||
|
soldoutFlag,
|
||||||
|
type = TYPE_HORIZONTAL,
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
const parsePriceInfo = (priceInfo) => {
|
const handleClick = useCallback(
|
||||||
const priceParts = priceInfo
|
(productId) => {
|
||||||
.split("|")
|
onCardClick && onCardClick(productId);
|
||||||
.filter((part) => part !== "N")
|
},
|
||||||
.map((item) => item.trim());
|
[onCardClick, productId]
|
||||||
|
);
|
||||||
|
|
||||||
let originalPrice, discountedPrice, discountRate;
|
const parsePriceInfo = useCallback(
|
||||||
|
(priceInfo) => {
|
||||||
|
const priceParts = priceInfo
|
||||||
|
.split("|")
|
||||||
|
.filter((part) => part !== "N")
|
||||||
|
.map((item) => item.trim());
|
||||||
|
|
||||||
if (priceParts.length === 4) {
|
let originalPrice, discountedPrice, discountRate;
|
||||||
[originalPrice, discountedPrice, , discountRate] = priceParts;
|
|
||||||
} else if (priceParts.length === 2) {
|
|
||||||
[originalPrice, discountedPrice] = priceParts;
|
|
||||||
discountRate = null;
|
|
||||||
} else {
|
|
||||||
originalPrice = null;
|
|
||||||
discountedPrice = null;
|
|
||||||
discountRate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { originalPrice, discountedPrice, discountRate };
|
if (priceParts.length === 4) {
|
||||||
};
|
[originalPrice, discountedPrice, , discountRate] = priceParts;
|
||||||
|
} else if (priceParts.length === 2) {
|
||||||
|
[originalPrice, discountedPrice] = priceParts;
|
||||||
|
discountRate = null;
|
||||||
|
} else {
|
||||||
|
originalPrice = null;
|
||||||
|
discountedPrice = null;
|
||||||
|
discountRate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { originalPrice, discountedPrice, discountRate };
|
||||||
|
},
|
||||||
|
[priceInfo]
|
||||||
|
);
|
||||||
|
|
||||||
const { originalPrice, discountedPrice, discountRate } =
|
const { originalPrice, discountedPrice, discountRate } =
|
||||||
parsePriceInfo(priceInfo);
|
parsePriceInfo(priceInfo);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SpottableItemList {...rest} className={css.container}>
|
<SpottableComponent
|
||||||
{rankOrd && (
|
{...rest}
|
||||||
<div className={css.rankOrd}>
|
className={classNames(
|
||||||
<span>TOP</span>
|
type === TYPE_HORIZONTAL && css.horizontal,
|
||||||
<strong>{rankOrd}</strong>
|
type === TYPE_VERTICAL && css.vertical
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
<div className={css.imageContainer}>
|
onClick={() => handleClick(productId)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
<img src={imageSource} alt={imageAlt} />
|
<img src={imageSource} alt={imageAlt} />
|
||||||
{discountRate && (
|
{discountRate && <span>{discountRate}</span>}
|
||||||
<div className={css.discountBanner}>{discountRate}</div>
|
{soldoutFlag && soldoutFlag === "N" && <div>{$L(SOLD_OUT_STRING)}</div>}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className={css.descContainer}>
|
<div>
|
||||||
<h1 className={css.productName}>{productName}</h1>
|
<h3>{productName}</h3>
|
||||||
<p className={css.priceInfo}>
|
<p>
|
||||||
{discountedPrice}
|
{discountRate ? discountedPrice : originalPrice}
|
||||||
{originalPrice !== discountedPrice && (
|
{discountRate && <span>{discountRate}</span>}
|
||||||
<span className={css.originalPrice}>{originalPrice}</span>
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</SpottableItemList>
|
{isBestSeller && rank && (
|
||||||
|
<div>
|
||||||
|
{$L(TOP_STRING)}
|
||||||
|
<span>{rank}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SpottableComponent>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|||||||
@@ -1,90 +1,186 @@
|
|||||||
@import "../../style/CommonStyle.module.less";
|
@import "../../style/CommonStyle.module.less";
|
||||||
@import "../../style/utils.module.less";
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
.container {
|
/* horizontal type */
|
||||||
.size(@w: 324px, @h: 438px);
|
.horizontal {
|
||||||
|
/* normal */
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
.size(@w: 600px, @h: 236px);
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
border: 1px solid @COLOR_GRAY02;
|
border: solid 1px @COLOR_GRAY02;
|
||||||
background-color: @COLOR_WHITE;
|
background-color: @COLOR_WHITE;
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:focus-within {
|
// left contents (image contetns)
|
||||||
border: 4px solid @PRIMARY_COLOR_RED;
|
> div:nth-child(1) {
|
||||||
padding: 15px;
|
|
||||||
box-shadow: 0 0 50px 0 rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rankOrd {
|
|
||||||
.size(@w:78px, @h:102px);
|
|
||||||
.position(@position: absolute, @top: 0, @left: 18px);
|
|
||||||
.flex(@justifyCenter: space-between, @direction: column);
|
|
||||||
padding: 12px 12px 18px;
|
|
||||||
background: @COLOR_GRAY07;
|
|
||||||
z-index: 2;
|
|
||||||
border-radius: 0 0 50px 50px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: @COLOR_WHITE;
|
|
||||||
> span {
|
|
||||||
.font (@fontFamily:Roboto, @fontSize:24px);
|
|
||||||
}
|
|
||||||
> strong {
|
|
||||||
.font (@fontFamily:Arial, @fontSize:42px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus-within {
|
|
||||||
.rankOrd {
|
|
||||||
top: -3px;
|
|
||||||
left: 15px;
|
|
||||||
background: @PRIMARY_COLOR_RED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.imageContainer {
|
|
||||||
.size(@w: 288px, @h: 288px);
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
color: @COLOR_WHITE;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
.size(@w: 100%, @h: 100%);
|
.size(@w: 200px, @h: 200px);
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: center;
|
border: solid 1px #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discountBanner {
|
// discount rate
|
||||||
|
span {
|
||||||
|
.position(@position: absolute, @right: 12px, @bottom: 12px);
|
||||||
.size(@w: 60px, @h: 60px);
|
.size(@w: 60px, @h: 60px);
|
||||||
.flex();
|
border-radius: 60px;
|
||||||
.position(@position: absolute, @bottom: 12px, @right: 12px);
|
|
||||||
background-color: @PRIMARY_COLOR_RED;
|
background-color: @PRIMARY_COLOR_RED;
|
||||||
color: @COLOR_WHITE;
|
.font(@fontFamily: "ArialBold", @fontSize:26px);
|
||||||
border-radius: 50%;
|
text-align: center;
|
||||||
padding: 5px 10px;
|
line-height: 60px;
|
||||||
font-size: 26px;
|
}
|
||||||
|
|
||||||
|
// sold out
|
||||||
|
div {
|
||||||
|
.position(@position: absolute, @top: 0, @right: 0);
|
||||||
|
.flex();
|
||||||
|
.size(@w: 200px, @h: 200px);
|
||||||
|
background-color: rgba(26, 26, 26, 0.6);
|
||||||
|
.font(@fontFamily: @baseFontBold, @fontSize: 36px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.descContainer {
|
// right contents
|
||||||
.productName {
|
> div:nth-child(2) {
|
||||||
.font (@fontFamily:@baseFontBold, @fontSize:24px);
|
.flex(@direction: column, @justifyCenter: space-between, @alignCenter: flex-start);
|
||||||
.elip(@clamp:2);
|
flex-grow: 1;
|
||||||
line-height: 1.33;
|
padding: 12px 0;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
.font(@fontFamily: @baseFontBold, @fontSize: 24px);
|
||||||
color: @COLOR_GRAY06;
|
color: @COLOR_GRAY06;
|
||||||
margin-top: 14px;
|
.elip(@clamp:2);
|
||||||
|
word-break: break-all;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.priceInfo {
|
p {
|
||||||
.font (@fontFamily:@baseFontBold, @fontSize:30px);
|
.flex(@justifyCenter: flex-start);
|
||||||
margin-top: 9px;
|
gap: 5px;
|
||||||
|
.font(@fontFamily: @baseFontBold, @fontSize: 30px);
|
||||||
color: @PRIMARY_COLOR_RED;
|
color: @PRIMARY_COLOR_RED;
|
||||||
line-height: 0.93;
|
|
||||||
|
|
||||||
.originalPrice {
|
span {
|
||||||
.font (@fontFamily:@baseFont, @fontSize:18px);
|
.font(@fontFamily: @baseFont, @fontSize: 18px);
|
||||||
margin-left: 5px;
|
|
||||||
text-decoration: line-through;
|
|
||||||
color: @COLOR_GRAY04;
|
color: @COLOR_GRAY04;
|
||||||
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* focused */
|
||||||
|
&:focus-within {
|
||||||
|
border: solid 1px @PRIMARY_COLOR_RED;
|
||||||
|
box-shadow: inset 0 0 0 4px @PRIMARY_COLOR_RED,
|
||||||
|
0 0 50px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vertical type (Thumbnail) */
|
||||||
|
.vertical {
|
||||||
|
/* normal */
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
.size(@w: 324px, @h: 438px);
|
||||||
|
padding: 18px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: solid 1px @COLOR_GRAY02;
|
||||||
|
background-color: @COLOR_WHITE;
|
||||||
|
|
||||||
|
// top contents (image contetns)
|
||||||
|
> div:nth-child(1) {
|
||||||
|
position: relative;
|
||||||
|
.size(@w: 288px, @h: 288px);
|
||||||
|
color: @COLOR_WHITE;
|
||||||
|
|
||||||
|
img {
|
||||||
|
.size(@w: 288px, @h: 288px);
|
||||||
|
object-fit: cover;
|
||||||
|
border: solid 1px #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// discount rate
|
||||||
|
span {
|
||||||
|
.position(@position: absolute, @right: 12px, @bottom: 12px);
|
||||||
|
.size(@w: 60px, @h: 60px);
|
||||||
|
border-radius: 60px;
|
||||||
|
background-color: @PRIMARY_COLOR_RED;
|
||||||
|
.font(@fontFamily: "ArialBold", @fontSize:26px);
|
||||||
|
text-align: center;
|
||||||
|
line-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sold out
|
||||||
|
> div:nth-child(3) {
|
||||||
|
.position(@position: absolute, @top: 0, @right: 0);
|
||||||
|
.flex();
|
||||||
|
.size(@w: 288px, @h: 288px);
|
||||||
|
background-color: rgba(26, 26, 26, 0.6);
|
||||||
|
.font(@fontFamily: @baseFontBold, @fontSize: 36px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom contents
|
||||||
|
> div:nth-child(2) {
|
||||||
|
.flex(@direction: column, @alignCenter: flex-start);
|
||||||
|
gap: 6px;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
.font(@fontFamily: @baseFontBold, @fontSize: 24px);
|
||||||
|
color: @COLOR_GRAY06;
|
||||||
|
.elip(@clamp:2);
|
||||||
|
word-break: break-all;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
.flex(@justifyCenter: flex-start);
|
||||||
|
gap: 5px;
|
||||||
|
.font(@fontFamily: @baseFontBold, @fontSize: 30px);
|
||||||
|
color: @PRIMARY_COLOR_RED;
|
||||||
|
|
||||||
|
span {
|
||||||
|
.font(@fontFamily: @baseFont, @fontSize: 18px);
|
||||||
|
color: @COLOR_GRAY04;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// best sellet
|
||||||
|
> div:nth-child(3) {
|
||||||
|
.position(@position: absolute, @top: -1px, @left: 18px);
|
||||||
|
.flex(@direction: column, @justifyCenter: space-between);
|
||||||
|
.size(@w: 79px, @h: 102px);
|
||||||
|
padding: 12px 12px 18px;
|
||||||
|
background-color: @COLOR_GRAY07;
|
||||||
|
.font(@fontFamily: @robotoFontBold, @fontSize: 24px);
|
||||||
|
color: @COLOR_WHITE;
|
||||||
|
border-bottom-left-radius: 79px;
|
||||||
|
border-bottom-right-radius: 79px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
.font(@fontFamily: @arialFontBold, @fontSize: 42px);
|
||||||
|
font-size: 42px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* focused */
|
||||||
|
&:focus-within {
|
||||||
|
border: solid 1px @PRIMARY_COLOR_RED;
|
||||||
|
box-shadow: inset 0 0 0 4px @PRIMARY_COLOR_RED,
|
||||||
|
0 0 50px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
|
||||||
|
// best seller
|
||||||
|
div:nth-child(3) {
|
||||||
|
background-color: @PRIMARY_COLOR_RED;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user