[ImagePanel] overlay UI 구현 및 MainView 추가

Detail Notes :
This commit is contained in:
younghoon100.park
2024-04-18 21:31:03 +09:00
parent 609d535926
commit 844b697e39
20 changed files with 547 additions and 36 deletions

View File

@@ -24,6 +24,7 @@ export const panel_names = {
PLAYER_PANEL: "playerpanel",
CHECKOUT_PANEL: "checkoutpanel",
THEME_CURATION_PANEL: "themeCurationPanel",
IMAGE_PANEL: "imagepanel",
// error
ERROR_PANEL: "errorpanel",

View File

@@ -524,23 +524,23 @@ export default function FeaturedBrandsPanel() {
dispatch(setHidePopup());
}, [dispatch]);
// const [brandLayoutInfo2, setBrandLayoutInfo2] = useState([]);
const [brandLayoutInfo2, setBrandLayoutInfo2] = useState([]);
// useEffect(() => {
// if (brandLayoutInfo) {
// setBrandLayoutInfo2([
// ...brandLayoutInfo,
// ...[
// {
// expsOrd: 2,
// patnrId: "1",
// shptmBrndOptTpCd: "BRD00109",
// shptmBrndOptTpNm: "Showroom",
// },
// ],
// ]);
// }
// }, [brandLayoutInfo]);
useEffect(() => {
if (brandLayoutInfo) {
setBrandLayoutInfo2([
...brandLayoutInfo,
...[
{
expsOrd: 2,
patnrId: "1",
shptmBrndOptTpCd: "BRD00109",
shptmBrndOptTpNm: "Showroom",
},
],
]);
}
}, [brandLayoutInfo]);
return (
<TPanel className={css.tPanel}>
@@ -717,14 +717,14 @@ export default function FeaturedBrandsPanel() {
)}
{hasTemplateCodeWithValue(
brandLayoutInfo,
brandLayoutInfo2,
TEMPLATE_CODE_CONF.SHOWROOM
) &&
shouldRenderComponent(brandShowroomInfo) && (
<Showroom
brandShowroomInfo={brandShowroomInfo}
order={getOrderByValue(
brandLayoutInfo,
brandLayoutInfo2,
TEMPLATE_CODE_CONF.SHOWROOM
)}
scrollTopBody={scrollTopBody}

View File

@@ -53,20 +53,13 @@ export default memo(function Showroom({
/>
{selectedRoomThemeInfos &&
selectedRoomThemeInfos.map(
(
{ roomThemeProducts, themeExpsOrd, themeId, themeImgUrl, themeNm },
contentsIndex
) => (
(selectedRoomThemeInfoItem, contentsIndex) => (
<ShowroomContents
key={"room-theme-infos-" + contentsIndex}
roomThemeProducts={roomThemeProducts}
scrollTopBody={scrollTopBody}
selectedPatnrId={selectedPatnrId}
selectedRoomId={selectedRoomId}
themeExpsOrd={themeExpsOrd}
themeId={themeId}
themeImgUrl={themeImgUrl}
themeNm={themeNm}
selectedRoomThemeInfoItem={selectedRoomThemeInfoItem}
/>
)
)}

View File

@@ -23,14 +23,10 @@ const Container = SpotlightContainerDecorator(
);
export default function ShowroomContents({
roomThemeProducts,
scrollTopBody,
selectedPatnrId,
selectedRoomId,
themeExpsOrd,
themeId,
themeImgUrl,
themeNm,
selectedRoomThemeInfoItem,
}) {
const { scrollTopByDistance } = useScrollTopByDistance();
@@ -40,6 +36,9 @@ export default function ShowroomContents({
const { cursorVisible } = useSelector((state) => state.common.appStatus);
const { roomThemeProducts, themeExpsOrd, themeId, themeImgUrl, themeNm } =
selectedRoomThemeInfoItem;
useEffect(() => {
const containerId = "showroom-contents-id-" + themeExpsOrd;
const container = document.getElementById(containerId);
@@ -88,12 +87,15 @@ export default function ShowroomContents({
dispatch(
pushPanel({
name: panel_names.PLAYER_PANEL,
// pyh Todo, edit panelInfo
panelInfo: {},
name: panel_names.IMAGE_PANEL,
panelInfo: {
roomId: selectedRoomId,
themeId,
themeExpsOrd,
},
})
);
}, [dispatch, panelInfo]);
}, [dispatch, panelInfo, selectedRoomId, themeExpsOrd, themeId]);
const handleFocus = useCallback(() => {
if (cursorVisible) {

View File

@@ -0,0 +1,74 @@
import React, { useCallback } from "react";
import classNames from "classnames";
import { useDispatch } from "react-redux";
import Spottable from "@enact/spotlight/Spottable";
import Marquee from "@enact/ui/Marquee";
import { popPanel } from "../../../actions/panelActions";
import css from "./ImageOverlayContents.module.less";
const SpottableButton = Spottable("button");
export default function ImageOverlayContents({
selectedThemeInfosLength,
selectedThemeExpsOrd,
setSelectedThemeExpsOrd,
sideContentsVisible,
setSideContentsVisible,
themeNm,
}) {
const dispatch = useDispatch();
const handleClick = useCallback(() => {
dispatch(popPanel());
}, [dispatch]);
const handleUpClick = useCallback(() => {
setSelectedThemeExpsOrd((prev) => prev - 1);
}, [setSelectedThemeExpsOrd]);
const handleDownClick = useCallback(() => {
setSelectedThemeExpsOrd((prev) => prev + 1);
}, [setSelectedThemeExpsOrd]);
const handleArrowClick = useCallback(() => {
setSideContentsVisible((prev) => !prev);
}, [setSideContentsVisible]);
return (
<div className={css.container}>
<SpottableButton className={css.back} onClick={handleClick} />
<h2>
<Marquee className={css.marquee} marqueeOn="render">
{themeNm}
</Marquee>
</h2>
{selectedThemeExpsOrd !== 1 && (
// up
<SpottableButton className={css.up} onClick={handleUpClick} />
)}
{selectedThemeExpsOrd !== selectedThemeInfosLength && (
// down
<SpottableButton className={css.down} onClick={handleDownClick} />
)}
<SpottableButton
// arrow
className={classNames(
css.arrow,
sideContentsVisible ? css.close : css.open
)}
onClick={handleArrowClick}
/>
<SpottableButton
// reduce
className={css.redution}
onClick={handleClick}
/>
</div>
);
}

View File

@@ -0,0 +1,97 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.container {
display: flex;
align-items: center;
margin: 66px;
.back {
.size(@w: 60px, @h: 60px);
background-size: 60px 60px;
background-repeat: no-repeat;
background-image: url("../../../../assets/images/btn/btn-60-wh-back-nor@3x.png");
&:focus {
background-image: url("../../../../assets/images/btn/btn-60-wh-back-foc@3x.png");
}
}
> h2:nth-child(2) {
width: 1736px;
padding-left: 30px;
color: @BG_COLOR_05;
font-family: @baseFont;
font-size: 44px;
line-height: normal;
.elip(@clamp:1);
.marquee {
width: 100%;
}
}
.up {
position: absolute;
top: 12px;
left: 50%;
transform: translateX(-50%);
.size(@w: 48px, @h: 48px);
background-size: contain;
background-image: url("../../../../assets/images/btn/btn-wh-arrow-top-nor.svg");
&:focus {
background-image: url("../../../../assets/images/btn/btn-wh-arrow-top-foc.svg");
}
}
.down {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
.size(@w: 48px, @h: 48px);
background-size: contain;
background-image: url("../../../../assets/images/btn/btn-wh-arrow-down-nor.svg");
&:focus {
background-image: url("../../../../assets/images/btn/btn-wh-arrow-down-foc.svg");
}
}
.arrow {
.position(@position: absolute, @top: 50%);
.size(@w: 48px, @h: 144px);
transform: translateY(-50%);
&.close {
right: 600px;
background-image: url(../../../../assets/images/btn/btn-toclose-nor.svg);
&:focus {
background-image: url(../../../../assets/images/btn/btn-toclose-foc.svg);
}
}
&.open {
right: 0;
background-image: url(../../../../assets/images/btn/btn-toopen-nor.svg);
&:focus {
background-image: url(../../../../assets/images/btn/btn-toopen-foc.svg);
}
}
}
.redution {
.position(@position: absolute, @right: 60px, @bottom: 114px);
.size(@w: 78px, @h: 78px);
background-size: 78px 78px;
background-repeat: no-repeat;
background-image: url("../../../../assets/images/btn/btn-video-min-nor@3x.png");
&:focus {
.size(@w:78px, @h:78px);
background-color: rgba(199, 8, 80, 0.5);
border-radius: 50%;
}
}
}

View File

@@ -0,0 +1,6 @@
{
"main": "ImageOverlayContents.jsx",
"style": [
"ImageOverlayContents.module.less"
]
}

View File

@@ -0,0 +1,105 @@
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import CustomImage from "../../components/CustomImage/CustomImage";
import TPanel from "../../components/TPanel/TPanel";
import ImageOverlayContents from "./ImageOverlayContents/ImageOverlayContents";
import css from "./ImagePanel.module.less";
import ImageSideContents from "./ImageSideContents/ImageSideContents";
const Container = SpotlightContainerDecorator(
{ leaveFor: { right: "" }, enterTo: "last-focused" },
"div"
);
export default function ImagePanel({ ...rest }) {
const panelInfo = useSelector((state) => state.panels.panels[1]?.panelInfo);
const brandShowroomInfo = useSelector(
(state) => state.brand.brandShowroomData.data.brandShowroomInfo
);
const [selectedThemeInfo, setSelectedThemeInfo] = useState(null);
const [selectedThemeInfosLength, setSelectedThemeInfosLength] =
useState(null);
const [selectedThemeExpsOrd, setSelectedThemeExpsOrd] = useState(null);
const [sideContentsVisible, setSideContentsVisible] = useState(true);
useEffect(() => {
if (panelInfo) {
console.log(
"%c pyh panelInfo",
"background: orange; color:black ",
panelInfo
);
console.log(
"%c pyh panelInfo length",
"background: orange; color:black ",
Object.keys(panelInfo)?.length
);
setSelectedThemeInfo(
brandShowroomInfo
.find(({ roomId }) => roomId === panelInfo?.roomId)
?.roomThemeInfos.find(({ themeId }) => themeId === panelInfo?.themeId)
);
setSelectedThemeInfosLength(
brandShowroomInfo.find(({ roomId }) => roomId === panelInfo?.roomId)
?.roomThemeInfos?.length
);
setSelectedThemeExpsOrd(panelInfo?.themeExpsOrd);
}
}, [brandShowroomInfo, panelInfo]);
useEffect(() => {
setSelectedThemeInfo(
brandShowroomInfo
.find(({ roomId }) => roomId === panelInfo?.roomId)
?.roomThemeInfos.find(
({ themeExpsOrd }) => themeExpsOrd === selectedThemeExpsOrd
)
);
}, [brandShowroomInfo, panelInfo?.roomId, selectedThemeExpsOrd]);
useEffect(() => {
if (selectedThemeInfo) {
console.log("pyh selectedThemeInfo", selectedThemeInfo);
}
if (selectedThemeInfosLength) {
console.log("pyh selectedThemeInfosLength", selectedThemeInfosLength);
}
}, [selectedThemeInfo, selectedThemeInfosLength]);
return (
<TPanel className={css.tPanel} isTabActivated={false} {...rest}>
{selectedThemeInfo &&
selectedThemeExpsOrd &&
selectedThemeInfosLength && (
<Container className={css.container}>
<figure>
<CustomImage src={selectedThemeInfo.themeImgUrl} alt="" />
</figure>
<ImageOverlayContents
themeNm={selectedThemeInfo.themeNm}
selectedThemeInfosLength={selectedThemeInfosLength}
selectedThemeExpsOrd={selectedThemeExpsOrd}
setSelectedThemeExpsOrd={setSelectedThemeExpsOrd}
sideContentsVisible={sideContentsVisible}
setSideContentsVisible={setSideContentsVisible}
/>
{sideContentsVisible && (
<ImageSideContents selectedThemeInfo={selectedThemeInfo} />
)}
</Container>
)}
</TPanel>
);
}

View File

@@ -0,0 +1,44 @@
@import "../../style//CommonStyle.module.less";
@import "../../style//utils.module.less";
.tPanel {
position: relative;
.container {
> figure:nth-child(1) {
.position(@position: absolute, @top: 0, @right: 0, @bottom: 0, @left: 0);
z-index: -1;
.size(@w: 100%, @h: 1080px);
&::before {
.position(@position: absolute, @top: 0, @right: 0, @left: 0);
.flex(@direction: column, @justifyCenter: space-between, @alignCenter: flex-start);
.size(@w: 100%, @h: 564px);
background-image: linear-gradient(
to top,
transparent 55%,
rgba(0, 0, 0, 0.7)
);
content: "";
}
&::after {
.position(@position: absolute, @right: 0, @left: 0, @bottom: 0);
.flex(@direction: column, @justifyCenter: space-between, @alignCenter: flex-start);
.size(@w: 100%, @h: 564px);
background-image: linear-gradient(
to bottom,
transparent 55%,
rgba(0, 0, 0, 0.8)
);
content: "";
}
> img {
position: absolute;
z-index: -1;
.size(@w: 100%, @h: 1080px);
}
}
}
}

View File

@@ -0,0 +1,45 @@
import React, { useCallback, useState } from "react";
import TButtonTab, {
LIST_TYPE,
} from "../../../components/TButtonTab/TButtonTab";
import { $L } from "../../../utils/helperMethods";
import css from "./ImageSideContents.module.less";
import RoomThemeProductList from "./RoomThemeProductList/RoomThemeProductList";
import ShopNowProductList from "./ShopNowProductList/ShopNowProductList";
const tabList = [$L("SHOP NOW"), $L("ROOM THEME")];
export default function ImageSideContents({ selectedThemeInfo }) {
const [tabIndex, setTabIndex] = useState(0);
// const { roomThemeProducts, themeExpsOrd, themeId, themeImgUrl, theemeNm } =
// selectedThemeInfo;
const handleItemClick = useCallback(
({ index }) => {
if (index === tabIndex) {
return;
}
setTabIndex(index);
},
[tabIndex]
);
return (
<div className={css.container}>
<TButtonTab
contents={tabList}
className={css.tButtonTab}
listType={LIST_TYPE.small}
onItemClick={handleItemClick}
selectedIndex={tabIndex}
/>
{tabIndex === 0 ? (
<ShopNowProductList selectedThemeInfo={selectedThemeInfo} />
) : (
<RoomThemeProductList />
)}
</div>
);
}

View File

@@ -0,0 +1,23 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.container {
.position(@position: absolute, @top: 0, @right: 0);
.size(@w: 600px, @h: 100%);
padding: 48px 30px 0 30px;
z-index: 999;
background: #2c343f;
.tButtonTab {
overflow: hidden;
width: auto;
margin-bottom: 24px;
border-radius: 12px;
> div {
.size(@w:270px, @h:66px);
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
}

View File

@@ -0,0 +1,5 @@
import React from "react";
export default function RoomThemeProductList() {
return <div>RoomThemeProductList</div>;
}

View File

@@ -0,0 +1,6 @@
{
"main": "RoomThemeProductList.jsx",
"style": [
"RoomThemeProductList.module.less"
]
}

View File

@@ -0,0 +1,67 @@
import React, { useCallback } from "react";
import TItemCard, { TYPES } from "../../../../components/TItemCard/TItemCard";
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../../hooks/useScrollTo";
import css from "./ShopNowProductList.module.less";
export default function ShopNowProductList({ selectedThemeInfo }) {
const {
getScrollTo,
// scrollTop
} = useScrollTo();
const {
roomThemeProducts,
// themeExpsOrd,
// themeId,
// themeImgUrl,
// theemeNm
} = selectedThemeInfo;
const renderItem = useCallback(
({ index, ...rest }) => {
const {
offerInfo,
// patnrId,
// prdtExpsOrd,
prdtId,
prdtImgUrl,
prdtNm,
priceInfo,
} = roomThemeProducts[index];
return (
<TItemCard
className={css.tItemCard}
imageAlt={prdtNm}
imageSource={prdtImgUrl}
// onClick={handleClick}
offerInfo={offerInfo}
priceInfo={priceInfo}
productId={prdtId}
productName={prdtNm}
type={TYPES.horizontal}
{...rest}
/>
);
},
[roomThemeProducts]
);
return (
<div className={css.container}>
{roomThemeProducts && (
<TVirtualGridList
cbScrollTo={getScrollTo}
className={css.tVirtualGridList}
dataSize={roomThemeProducts.length}
itemHeight={236}
itemWidth={540}
spacing={12}
renderItem={renderItem}
/>
)}
</div>
);
}

View File

@@ -0,0 +1,23 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
// itemSize = width: 540px, height: 236px
.container {
position: relative;
overflow: hidden;
.size(@w: 540px, @h: 100%);
// tVirtualGridListContainer
> div:nth-child(1) {
.size(@w: 540px, @h: 548px);
// > div:nth-child(1) {
// padding: 22px 18px;
// }
.tItemCard {
width: 540px;
}
}
}

View File

@@ -0,0 +1,6 @@
{
"main": "ShopNowProductList.jsx",
"style": [
"ShopNowProductList.module.less"
]
}

View File

@@ -0,0 +1,6 @@
{
"main": "ImageSideContents.jsx",
"style": [
"ImageSideContents.module.less"
]
}

View File

@@ -0,0 +1,6 @@
{
"main": "ImagePanel.jsx",
"style": [
"ImagePanel.module.less"
]
}

View File

@@ -23,6 +23,7 @@ import ErrorPanel from "../ErrorPanel/ErrorPanel";
import FeaturedBrandsPanel from "../FeaturedBrandsPanel/FeaturedBrandsPanel";
import HomePanel from "../HomePanel/HomePanel";
import HotPicksPanel from "../HotPicksPanel/HotPicksPanel";
import ImagePanel from "../ImagePanel/ImagePanel";
import IntroPanel from "../IntroPanel/IntroPanel";
import LoadingPanel from "../LoadingPanel/LoadingPanel";
import MyPagePanel from "../MyPagePanel/MyPagePanel";
@@ -54,6 +55,7 @@ const panelMap = {
[Config.panel_names.CHECKOUT_PANEL]: CheckOutPanel,
[Config.panel_names.WELCOME_EVENT_PANEL]: WelcomeEventPanel,
[Config.panel_names.THEME_CURATION_PANEL]: ThemeCurationPanel,
[Config.panel_names.IMAGE_PANEL]: ImagePanel,
};
export default function MainView() {