[251022] feat: HowAboutThese
🕐 커밋 시간: 2025. 10. 22. 18:46:19 📊 변경 통계: • 총 파일: 9개 • 추가: +1줄 • 삭제: -1줄 📁 추가된 파일: + com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.figma.jsx + com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.jsx + com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.module.less + com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.small.figama.jsx + com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.small.jsx + com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.small.module.less + com.twin.app.shoptime/src/views/SearchPanel/SearchResults.new.v2.jsx + com.twin.app.shoptime/src/views/SearchPanel/SearchResults.new.v2.module.less 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.new.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.jsx (javascript): ✅ Added: HowAboutThese() 📄 com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.module.less (unknown): ✅ Added: gradient(), translateY() 📄 com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.small.jsx (javascript): ✅ Added: HowAboutTheseSmall() 📄 com.twin.app.shoptime/src/views/SearchPanel/HowAboutThese/HowAboutThese.small.module.less (unknown): ✅ Added: translateY(), gradient() 📄 com.twin.app.shoptime/src/views/SearchPanel/SearchResults.new.v2.jsx (javascript): ✅ Added: SearchResultsNew(), getButtonTabList(), upBtnClick() 📄 com.twin.app.shoptime/src/views/SearchPanel/SearchResults.new.v2.module.less (unknown): ✅ Added: translateY(), child()
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '860px',
|
||||
paddingTop: 32,
|
||||
paddingBottom: 30,
|
||||
paddingLeft: 60,
|
||||
paddingRight: 60,
|
||||
left: 0,
|
||||
top: 0,
|
||||
position: 'absolute',
|
||||
background:
|
||||
'linear-gradient(360deg, rgba(221.25, 221.25, 221.25, 0) 0%, rgba(221.25, 221.25, 221.25, 0.85) 57%, rgba(221.25, 221.25, 221.25, 0.90) 80%, rgba(221, 221, 221, 0.90) 100%), linear-gradient(360deg, rgba(221.25, 221.25, 221.25, 0) 35%, #DDDDDD 80%, #DDDDDD 100%)',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 19,
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
alignSelf: 'stretch',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: '1 1 0', height: 48, position: 'relative' }}>
|
||||
<img
|
||||
style={{ width: 48, height: 48, left: 0, top: 0, position: 'absolute' }}
|
||||
src="https://placehold.co/48x48"
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
left: 53,
|
||||
top: 10,
|
||||
position: 'absolute',
|
||||
borderBottom: '1px black solid',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
color: '#272727',
|
||||
fontSize: 24,
|
||||
fontFamily: 'Roboto',
|
||||
fontWeight: '700',
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
How about these?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: '1 1 0',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-end',
|
||||
gap: 10,
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: 200,
|
||||
height: 60,
|
||||
background: '#808080',
|
||||
borderRadius: 6,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
SEE MORE
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
alignSelf: 'stretch',
|
||||
overflow: 'hidden',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 15,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: 64,
|
||||
paddingLeft: 20,
|
||||
paddingRight: 20,
|
||||
paddingTop: 16,
|
||||
paddingBottom: 16,
|
||||
background: '#CE1C5E',
|
||||
boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
|
||||
borderRadius: 30,
|
||||
outline: '1px #C70850 solid',
|
||||
outlineOffset: '-1px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
gap: 20,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: '#EAEAEA',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
What are some luxury skincare products.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
background: 'white',
|
||||
borderRadius: 100,
|
||||
outline: '1px #DADADA solid',
|
||||
outlineOffset: '-1px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 10,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'black',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
Popular makeup gift sets uitable for mother.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
background: 'white',
|
||||
borderRadius: 100,
|
||||
outline: '1px #DADADA solid',
|
||||
outlineOffset: '-1px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 10,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'black',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
Which anti-aging cosmetics in 50s or 60s.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
background: 'white',
|
||||
borderRadius: 100,
|
||||
outline: '1px #DADADA solid',
|
||||
outlineOffset: '-1px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 10,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'black',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
Elegant fragrance or cosmetic bundles.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
background: 'white',
|
||||
borderRadius: 100,
|
||||
outline: '1px #DADADA solid',
|
||||
outlineOffset: '-1px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 10,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'black',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
What beauty brands offer special gift boxes.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
@@ -0,0 +1,126 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||
|
||||
import { getShopperHouseSearch } from '../../../actions/searchActions';
|
||||
import css from './HowAboutThese.module.less';
|
||||
|
||||
const OverlayContainer = SpotlightContainerDecorator(
|
||||
{
|
||||
enterTo: 'default-element',
|
||||
restrict: 'self-only',
|
||||
},
|
||||
'div'
|
||||
);
|
||||
|
||||
const SpottableBubble = Spottable('div');
|
||||
const SpottableSeeMoreButton = Spottable('div');
|
||||
|
||||
const HowAboutThese = ({ relativeQueries = [], onQueryClick, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// 기본 relativeQueries가 없는 경우를 위한 fallback
|
||||
const queries = useMemo(() => {
|
||||
return relativeQueries.length > 0
|
||||
? relativeQueries
|
||||
: [
|
||||
'What are some luxury skincare products',
|
||||
'Popular makeup gift sets suitable for mother',
|
||||
'Which anti-aging cosmetics in 50s or 60s',
|
||||
'Elegant fragrance or cosmetic bundles',
|
||||
'What beauty brands offer special gift boxes',
|
||||
];
|
||||
}, [relativeQueries]);
|
||||
|
||||
// 검색어 클릭 핸들러
|
||||
const handleQueryClick = useCallback(
|
||||
(query) => {
|
||||
console.log('[HowAboutThese] Query clicked:', query);
|
||||
|
||||
// 외부에서 전달된 onQueryClick이 있으면 사용
|
||||
if (onQueryClick) {
|
||||
onQueryClick(query);
|
||||
return;
|
||||
}
|
||||
|
||||
// 기본적으로 ShopperHouse API를 통해 재검색
|
||||
dispatch(getShopperHouseSearch(query));
|
||||
|
||||
// 팝업 닫기
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
[dispatch, onQueryClick, onClose]
|
||||
);
|
||||
|
||||
// "COLLAPSE" 버튼 클릭 핸들러 (Full 버전을 Small로 축소)
|
||||
const handleCollapseClick = useCallback(() => {
|
||||
console.log('[HowAboutThese] Collapse clicked - 축소하여 Small 버전으로 전환');
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
}, [onClose]);
|
||||
|
||||
// 첫 번째 쿼리는 특별 스타일 (핑크색 버블)
|
||||
const firstQuery = queries[0];
|
||||
const remainingQueries = queries.slice(1);
|
||||
|
||||
return (
|
||||
<OverlayContainer className={css.container}>
|
||||
{/* Header Section */}
|
||||
<div className={css.header}>
|
||||
<div className={css.headerLeft}>
|
||||
<div className={css.headerContent}>
|
||||
{/* 아이콘 자리 - 현재는 비워둠 */}
|
||||
<div className={css.iconPlaceholder} />
|
||||
<div className={css.titleContainer}>
|
||||
<div className={css.title}>How about these?</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.headerRight}>
|
||||
<SpottableSeeMoreButton className={css.seeMoreButton} onClick={handleCollapseClick}>
|
||||
<span className={css.seeMoreText}>COLLAPSE</span>
|
||||
</SpottableSeeMoreButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bubbles Section */}
|
||||
<div className={css.bubblesContainer}>
|
||||
{/* 첫 번째 버블 - 핑크색 */}
|
||||
{firstQuery && (
|
||||
<SpottableBubble
|
||||
className={css.bubblePrimary}
|
||||
onClick={() => handleQueryClick(firstQuery)}
|
||||
>
|
||||
<span className={css.bubbleText}>{firstQuery}</span>
|
||||
</SpottableBubble>
|
||||
)}
|
||||
|
||||
{/* 나머지 버블들 - 흰색 */}
|
||||
{remainingQueries.map((query, index) => (
|
||||
<SpottableBubble
|
||||
key={`query-${index}`}
|
||||
className={css.bubbleSecondary}
|
||||
onClick={() => handleQueryClick(query)}
|
||||
>
|
||||
<span className={css.bubbleText}>{query}</span>
|
||||
</SpottableBubble>
|
||||
))}
|
||||
</div>
|
||||
</OverlayContainer>
|
||||
);
|
||||
};
|
||||
|
||||
HowAboutThese.propTypes = {
|
||||
relativeQueries: PropTypes.array,
|
||||
onQueryClick: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
};
|
||||
|
||||
export default HowAboutThese;
|
||||
@@ -0,0 +1,209 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 860px;
|
||||
padding: 30px 60px;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 19px;
|
||||
background: linear-gradient(
|
||||
360deg,
|
||||
rgba(221.25, 221.25, 221.25, 0) 0%,
|
||||
rgba(221.25, 221.25, 221.25, 0.85) 57%,
|
||||
rgba(221.25, 221.25, 221.25, 0.9) 80%,
|
||||
rgba(221, 221, 221, 0.9) 100%
|
||||
),
|
||||
linear-gradient(
|
||||
360deg,
|
||||
rgba(221.25, 221.25, 221.25, 0) 35%,
|
||||
#DDDDDD 80%,
|
||||
#DDDDDD 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* Header Section */
|
||||
.header {
|
||||
align-self: stretch;
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
flex: 1;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.headerContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.iconPlaceholder {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
/* TODO: Add icon styling when needed */
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
position: absolute;
|
||||
left: 53px;
|
||||
top: 10px;
|
||||
border-bottom: 1px solid black;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #272727;
|
||||
font-size: 24px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
flex: 1 1 0;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.seeMoreButton {
|
||||
width: 200px;
|
||||
height: 60px;
|
||||
background: #808080;
|
||||
border-radius: 6px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid #00a0e9;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.seeMoreText {
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-family: 'LG Smart UI', sans-serif;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Bubbles Section */
|
||||
.bubblesContainer {
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Primary Bubble (Pink) */
|
||||
.bubblePrimary {
|
||||
height: 64px;
|
||||
padding: 16px 20px;
|
||||
background: #CE1C5E;
|
||||
border-radius: 30px;
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
||||
outline: 1px solid #C70850;
|
||||
outline-offset: -1px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid #00a0e9;
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0 0 0 2px #00a0e9;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #b91a52;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Secondary Bubbles (White) */
|
||||
.bubbleSecondary {
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 100px;
|
||||
outline: 1px solid #DADADA;
|
||||
outline-offset: -1px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid #00a0e9;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f8f8f8;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.bubbleText {
|
||||
color: #EAEAEA;
|
||||
font-size: 24px;
|
||||
font-family: 'LG Smart UI', sans-serif;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
word-wrap: break-word;
|
||||
width: 100%;
|
||||
|
||||
.bubbleSecondary & {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
paddingLeft: 60,
|
||||
paddingRight: 60,
|
||||
paddingTop: 30,
|
||||
paddingBottom: 30,
|
||||
background: '#DDDDDD',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
gap: 19,
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div style={{ width: 254, height: 48, position: 'relative' }}>
|
||||
<img
|
||||
style={{ width: 48, height: 48, left: 0, top: 0, position: 'absolute' }}
|
||||
src="https://placehold.co/48x48"
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
left: 53,
|
||||
top: 10,
|
||||
position: 'absolute',
|
||||
borderBottom: '1px black solid',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
color: '#272727',
|
||||
fontSize: 24,
|
||||
fontFamily: 'Roboto',
|
||||
fontWeight: '700',
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
How about these?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: '1 1 0',
|
||||
overflow: 'hidden',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 15,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
background: 'white',
|
||||
borderRadius: 100,
|
||||
outline: '1px #DADADA solid',
|
||||
outlineOffset: '-1px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 10,
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'black',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
What are some luxury skincare products.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
background: 'white',
|
||||
borderRadius: 100,
|
||||
outline: '1px #DADADA solid',
|
||||
outlineOffset: '-1px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 10,
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'black',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
Popular makeup gift sets uitable for mother.
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ width: 1031, height: 64, position: 'relative' }}>
|
||||
<div
|
||||
style={{
|
||||
width: 508,
|
||||
height: 64,
|
||||
padding: 20,
|
||||
left: 0,
|
||||
top: 0,
|
||||
position: 'absolute',
|
||||
background: 'linear-gradient(90deg, black 0%, rgba(102, 102, 102, 0) 60%)',
|
||||
borderRadius: 100,
|
||||
border: '1px #DADADA solid',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
left: 0,
|
||||
top: 0,
|
||||
position: 'absolute',
|
||||
background: 'white',
|
||||
borderRadius: 100,
|
||||
outline: '1px #DADADA solid',
|
||||
outlineOffset: '-1px',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
gap: 10,
|
||||
display: 'inline-flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'black',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
Popular makeup gift sets uitable for mother.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: 200,
|
||||
height: 60,
|
||||
background: '#808080',
|
||||
borderRadius: 6,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
fontSize: 24,
|
||||
fontFamily: 'LG Smart UI',
|
||||
fontWeight: '700',
|
||||
lineHeight: 24,
|
||||
wordWrap: 'break-word',
|
||||
}}
|
||||
>
|
||||
SEE MORE
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
@@ -0,0 +1,109 @@
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
|
||||
import { getShopperHouseSearch } from '../../../actions/searchActions';
|
||||
import css from './HowAboutThese.small.module.less';
|
||||
|
||||
const SpottableBubble = Spottable('div');
|
||||
const SpottableSeeMoreButton = Spottable('div');
|
||||
|
||||
const HowAboutTheseSmall = ({ relativeQueries = [], onQueryClick, onSeeMoreClick }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// 기본 relativeQueries가 없는 경우를 위한 fallback
|
||||
const queries =
|
||||
relativeQueries.length > 0
|
||||
? relativeQueries
|
||||
: [
|
||||
'What are some luxury skincare products',
|
||||
'Popular makeup gift sets suitable for mother',
|
||||
'Which anti-aging cosmetics in 50s or 60s',
|
||||
];
|
||||
|
||||
// 검색어 클릭 핸들러
|
||||
const handleQueryClick = useCallback(
|
||||
(query) => {
|
||||
console.log('[HowAboutTheseSmall] Query clicked:', query);
|
||||
|
||||
// 외부에서 전달된 onQueryClick이 있으면 사용
|
||||
if (onQueryClick) {
|
||||
onQueryClick(query);
|
||||
return;
|
||||
}
|
||||
|
||||
// 기본적으로 ShopperHouse API를 통해 재검색
|
||||
dispatch(getShopperHouseSearch(query));
|
||||
},
|
||||
[dispatch, onQueryClick]
|
||||
);
|
||||
|
||||
// "SEE MORE" 버튼 클릭 핸들러
|
||||
const handleSeeMoreClick = useCallback(() => {
|
||||
console.log('[HowAboutTheseSmall] See More clicked');
|
||||
|
||||
// 외부에서 전달된 onSeeMoreClick이 있으면 사용
|
||||
if (onSeeMoreClick) {
|
||||
onSeeMoreClick();
|
||||
return;
|
||||
}
|
||||
|
||||
// 기본 동작: 확장된 뷰 표시 (나중에 구현)
|
||||
console.log('[HowAboutTheseSmall] TODO: Show expanded view');
|
||||
}, [onSeeMoreClick]);
|
||||
|
||||
// 첫 번째 두 개의 쿼리만 표시 (small 버전)
|
||||
const displayQueries = queries.slice(0, 2);
|
||||
|
||||
return (
|
||||
<div className={css.container}>
|
||||
{/* Header Section */}
|
||||
<div className={css.header}>
|
||||
{/* 아이콘 자리 - 현재는 비워둠 */}
|
||||
<div className={css.iconPlaceholder} />
|
||||
<div className={css.titleContainer}>
|
||||
<div className={css.title}>How about these?</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bubbles Section */}
|
||||
<div className={css.bubblesContainer}>
|
||||
{displayQueries.map((query, index) => (
|
||||
<SpottableBubble
|
||||
key={`query-${index}`}
|
||||
className={css.bubble}
|
||||
onClick={() => handleQueryClick(query)}
|
||||
>
|
||||
<div className={css.bubbleText}>{query}</div>
|
||||
</SpottableBubble>
|
||||
))}
|
||||
|
||||
{/* Fade 효과를 위한 추가 요소 (피그마 디자인 참고) */}
|
||||
{queries.length > 2 && (
|
||||
<div className={css.fadeContainer}>
|
||||
<div className={css.fadeOverlay} />
|
||||
<SpottableBubble className={css.bubble}>
|
||||
<div className={css.bubbleText}>{queries[2]}</div>
|
||||
</SpottableBubble>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* See More Button */}
|
||||
<SpottableSeeMoreButton className={css.seeMoreButton} onClick={handleSeeMoreClick}>
|
||||
<div className={css.seeMoreText}>SEE MORE</div>
|
||||
</SpottableSeeMoreButton>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
HowAboutTheseSmall.propTypes = {
|
||||
relativeQueries: PropTypes.array,
|
||||
onQueryClick: PropTypes.func,
|
||||
onSeeMoreClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default HowAboutTheseSmall;
|
||||
@@ -0,0 +1,151 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 30px 60px; // 상하 30px, 좌우 60px
|
||||
background: #DDDDDD;
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 19px;
|
||||
}
|
||||
|
||||
/* Header Section */
|
||||
.header {
|
||||
width: 254px;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
flex-shrink: 0; // 고정 너비 유지
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.iconPlaceholder {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
flex-shrink: 0;
|
||||
/* TODO: Add icon styling when needed */
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
margin-left: 5px; // 아이콘과 제목 사이 간격 (53 - 48 = 5)
|
||||
border-bottom: 1px solid black;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
white-space: nowrap; // ⭐ 1줄로 표시
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #272727;
|
||||
font-size: 24px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
white-space: nowrap; // ⭐ 1줄로 표시
|
||||
line-height: 1; // 텍스트 높이 조정
|
||||
}
|
||||
|
||||
/* Bubbles Section */
|
||||
.bubblesContainer {
|
||||
flex: 1 1 0;
|
||||
overflow: hidden;
|
||||
display: flex; // horizontal layout
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 100px;
|
||||
outline: 1px solid #DADADA;
|
||||
outline-offset: -1px;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0; // 버블이 줄어들지 않도록
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid #00a0e9;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f8f8f8;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeContainer {
|
||||
width: 1031px;
|
||||
height: 64px;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fadeOverlay {
|
||||
width: 508px;
|
||||
height: 64px;
|
||||
padding: 20px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
background: linear-gradient(90deg, black 0%, rgba(102, 102, 102, 0) 60%);
|
||||
border-radius: 100px;
|
||||
border: 1px solid #DADADA;
|
||||
pointer-events: none; // 클릭 방지
|
||||
}
|
||||
|
||||
.fadeContainer .bubble {
|
||||
left: 0;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.bubbleText {
|
||||
text-align: center;
|
||||
color: black;
|
||||
font-size: 24px;
|
||||
font-family: 'LG Smart UI', sans-serif;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* See More Button */
|
||||
.seeMoreButton {
|
||||
width: 200px;
|
||||
height: 60px;
|
||||
background: #808080;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: center; // 피그마: center
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
flex-shrink: 0; // 고정 너비 유지
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid #00a0e9;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #666666;
|
||||
}
|
||||
}
|
||||
|
||||
.seeMoreText {
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-family: 'LG Smart UI', sans-serif;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ import usePrevious from '../../hooks/usePrevious';
|
||||
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../utils/Config';
|
||||
import SearchInputOverlay from './SearchInpuOverlay';
|
||||
import css from './SearchPanel.new.module.less';
|
||||
import SearchResultsNew from './SearchResults.new';
|
||||
import SearchResultsNew from './SearchResults.new.v2';
|
||||
import TInput, { ICONS, KINDS } from './TInput/TInput';
|
||||
import VoiceInputOverlay, { VOICE_MODES } from './VoiceInputOverlay/VoiceInputOverlay';
|
||||
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
|
||||
import downBtnImg from '../../../assets/images/btn/search_btn_down_arrow.png';
|
||||
import upBtnImg from '../../../assets/images/btn/search_btn_up_arrow.png';
|
||||
import CustomImage from '../../components/CustomImage/CustomImage';
|
||||
import TButtonTab, { LIST_TYPE } from '../../components/TButtonTab/TButtonTab';
|
||||
import TDropDown from '../../components/TDropDown/TDropDown';
|
||||
import TVirtualGridList from '../../components/TVirtualGridList/TVirtualGridList';
|
||||
import { $L } from '../../utils/helperMethods';
|
||||
import { SpotlightIds } from '../../utils/SpotlightIds';
|
||||
import css from './SearchResults.new.v2.module.less';
|
||||
import ItemCard from './SearchResultsNew/ItemCard';
|
||||
import ShowCard from './SearchResultsNew/ShowCard';
|
||||
import HowAboutThese from './HowAboutThese/HowAboutThese';
|
||||
import HowAboutTheseSmall from './HowAboutThese/HowAboutThese.small';
|
||||
|
||||
const ITEMS_PER_PAGE = 10;
|
||||
|
||||
// HowAboutThese 모드 상수
|
||||
export const HOW_ABOUT_THESE_MODES = {
|
||||
SMALL: 'small', // 작은 버전 (기본)
|
||||
FULL: 'full', // 전체 버전 (팝업)
|
||||
};
|
||||
|
||||
const SearchResultsNew = ({ itemInfo, showInfo, themeInfo, shopperHouseInfo, keywordClick }) => {
|
||||
// ShopperHouse 데이터를 ItemCard 형식으로 변환
|
||||
const convertedShopperHouseItems = useMemo(() => {
|
||||
if (!shopperHouseInfo || !shopperHouseInfo.results || shopperHouseInfo.results.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const resultData = shopperHouseInfo.results[0];
|
||||
const docs = resultData.docs || [];
|
||||
|
||||
return docs.map((doc) => {
|
||||
const contentId = doc.contentId;
|
||||
const tokens = contentId.split('_');
|
||||
const patnrId = tokens?.[4] || '';
|
||||
const prdtId = tokens?.[5] || '';
|
||||
|
||||
return {
|
||||
thumbnail: doc.thumbnail || doc.imgPath || '', // 이미지 경로 (API 필드명 수정)
|
||||
title: doc.title || doc.prdtName || '', // 제목
|
||||
dcPrice: doc.dcPrice || doc.price || '', // 할인가격
|
||||
price: doc.price || '', // 원가
|
||||
soldout: doc.soldout || 'N', // 품절 여부
|
||||
contentId, // 콘텐트 아이디
|
||||
reviewGrade: doc.reviewGrade || '', // 리뷰 점수
|
||||
partnerName: doc.partnerName || '', // 파트너 네임
|
||||
partnerLogo: doc.partnerLogo || '', // 파트너 로고 (API 명세서 추가)
|
||||
rankInfo: doc.rankInfo || 0, // 랭킹 정보 (API 명세서 추가)
|
||||
patnrId, // 파트너 아이디
|
||||
prdtId, // 상품 아이디
|
||||
// results 레벨 추가 정보
|
||||
searchId: resultData.searchId || '',
|
||||
sortingType: resultData.sortingType || '',
|
||||
rangeType: resultData.rangeType || '',
|
||||
};
|
||||
});
|
||||
}, [shopperHouseInfo]);
|
||||
const getButtonTabList = () => {
|
||||
// ShopperHouse 데이터가 있으면 그것을 사용, 없으면 기존 검색 결과 사용
|
||||
const itemLength = convertedShopperHouseItems?.length || itemInfo?.length || 0;
|
||||
const showLength = showInfo?.length || 0;
|
||||
|
||||
return [
|
||||
itemLength && $L(`ITEM (${itemLength})`),
|
||||
showLength && $L(`SHOWS (${showLength})`),
|
||||
].filter(Boolean);
|
||||
};
|
||||
|
||||
let buttonTabList = null;
|
||||
|
||||
//탭
|
||||
const [tab, setTab] = useState(0);
|
||||
//드롭다운
|
||||
const [dropDownTab, setDropDownTab] = useState(0);
|
||||
//표시할 아이템 개수
|
||||
const [visibleCount, setVisibleCount] = useState(ITEMS_PER_PAGE);
|
||||
|
||||
const [styleChange, setStyleChange] = useState(false);
|
||||
const filterMethods = [];
|
||||
const cbChangePageRef = useRef(null);
|
||||
|
||||
// HowAboutThese 모드 상태 관리
|
||||
const [howAboutTheseMode, setHowAboutTheseMode] = useState(HOW_ABOUT_THESE_MODES.SMALL);
|
||||
|
||||
// HowAboutThese 모드 전환 핸들러
|
||||
const handleShowFullHowAboutThese = useCallback(() => {
|
||||
setHowAboutTheseMode(HOW_ABOUT_THESE_MODES.FULL);
|
||||
}, []);
|
||||
|
||||
// HowAboutThese 닫기 핸들러 (Full -> Small)
|
||||
const handleCloseHowAboutThese = useCallback(() => {
|
||||
setHowAboutTheseMode(HOW_ABOUT_THESE_MODES.SMALL);
|
||||
}, []);
|
||||
|
||||
// 쿼리 클릭 핸들러 (Small 버전용)
|
||||
const handleSmallQueryClick = useCallback(
|
||||
(query) => {
|
||||
if (keywordClick) {
|
||||
keywordClick(query);
|
||||
}
|
||||
// 쿼리 클릭 시에는 모드 유지 (small 계속 표시)
|
||||
},
|
||||
[keywordClick]
|
||||
);
|
||||
|
||||
// 쿼리 클릭 핸들러 (Full 버전용)
|
||||
const handleFullQueryClick = useCallback(
|
||||
(query) => {
|
||||
if (keywordClick) {
|
||||
keywordClick(query);
|
||||
}
|
||||
setHowAboutTheseMode(HOW_ABOUT_THESE_MODES.SMALL);
|
||||
},
|
||||
[keywordClick]
|
||||
);
|
||||
|
||||
if (!buttonTabList) {
|
||||
buttonTabList = getButtonTabList();
|
||||
}
|
||||
|
||||
// 현재 탭의 데이터 가져오기 - ShopperHouse 데이터 우선
|
||||
const currentData = tab === 0 ? convertedShopperHouseItems || itemInfo : showInfo;
|
||||
|
||||
// 표시할 데이터 (처음부터 visibleCount 개수만큼)
|
||||
const displayedData = useMemo(() => {
|
||||
if (!currentData) return [];
|
||||
return currentData.slice(0, visibleCount);
|
||||
}, [currentData, visibleCount]);
|
||||
|
||||
// 더 불러올 데이터가 있는지 확인
|
||||
const hasMore = currentData && visibleCount < currentData.length;
|
||||
|
||||
const handleStyle = useCallback(() => {
|
||||
setStyleChange(true);
|
||||
}, []);
|
||||
|
||||
const handleStyleOut = useCallback(() => {
|
||||
setStyleChange(false);
|
||||
}, []);
|
||||
|
||||
//탭 클릭
|
||||
const handleButtonTabClick = useCallback(
|
||||
({ index }) => {
|
||||
if (index === tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTab(index);
|
||||
setVisibleCount(ITEMS_PER_PAGE); // 탭 변경시 표시 개수 리셋
|
||||
if (cbChangePageRef.current) {
|
||||
cbChangePageRef.current(0, false, false);
|
||||
}
|
||||
},
|
||||
[tab]
|
||||
);
|
||||
|
||||
//필터선택
|
||||
const handleSelectFilter = useCallback(
|
||||
({ selected }) => {
|
||||
if (selected === dropDownTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDropDownTab(selected);
|
||||
setVisibleCount(ITEMS_PER_PAGE); // 필터 변경시 표시 개수 리셋
|
||||
},
|
||||
[dropDownTab]
|
||||
);
|
||||
|
||||
const SpottableLi = Spottable('li');
|
||||
const SpottableDiv = Spottable('div');
|
||||
|
||||
// 맨 처음으로 이동 (위 버튼)
|
||||
const upBtnClick = () => {
|
||||
if (cbChangePageRef.current) {
|
||||
cbChangePageRef.current(0, true);
|
||||
}
|
||||
};
|
||||
|
||||
// 10개씩 추가 로드 (아래 버튼)
|
||||
const downBtnClick = useCallback(() => {
|
||||
if (hasMore) {
|
||||
setVisibleCount((prev) => prev + ITEMS_PER_PAGE);
|
||||
}
|
||||
}, [hasMore]);
|
||||
|
||||
// ProductCard 컴포넌트
|
||||
const renderItem = useCallback(
|
||||
({ index, ...rest }) => {
|
||||
const { bgImgPath, title, partnerLogo, partnerName, keyword } = themeInfo[index];
|
||||
return (
|
||||
<SpottableDiv
|
||||
key={`searchProduct-${index}`}
|
||||
className={css.productCard}
|
||||
spotlightId={`searchProduct-${index}`}
|
||||
{...rest}
|
||||
>
|
||||
<div className={css.productImageWrapper}>
|
||||
<img src={bgImgPath} alt={title} className={css.productImage} />
|
||||
</div>
|
||||
<div className={css.productInfo}>
|
||||
<div className={css.productBrandWrapper}>
|
||||
<img src={partnerLogo} alt={partnerName} className={css.brandLogo} />
|
||||
</div>
|
||||
<div className={css.productDetails}>
|
||||
{keyword && (
|
||||
<div className={css.brandName}>
|
||||
{keyword.map((item, keywordIndex) => (
|
||||
<span key={keywordIndex}># {item}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className={css.productTitle}>{title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</SpottableDiv>
|
||||
);
|
||||
},
|
||||
[themeInfo]
|
||||
);
|
||||
|
||||
// relativeQuerys 가져오기 (ShopperHouse API 응답)
|
||||
const relativeQuerys = useMemo(() => {
|
||||
if (shopperHouseInfo?.results?.[0]?.relativeQuerys) {
|
||||
return shopperHouseInfo.results[0].relativeQuerys;
|
||||
}
|
||||
// 기본값
|
||||
return ['Puppy food', 'Dog toy', 'Fitness'];
|
||||
}, [shopperHouseInfo]);
|
||||
|
||||
return (
|
||||
<div className={css.searchBox}>
|
||||
{/* HowAboutThese Small 버전 - 기본 인라인 표시 */}
|
||||
<HowAboutTheseSmall
|
||||
relativeQueries={relativeQuerys}
|
||||
onQueryClick={handleSmallQueryClick}
|
||||
onSeeMoreClick={handleShowFullHowAboutThese}
|
||||
/>
|
||||
|
||||
{/* HowAboutThese Full 버전 - 오버레이로 표시 */}
|
||||
{howAboutTheseMode === HOW_ABOUT_THESE_MODES.FULL && (
|
||||
<div className={css.howAboutTheseOverlay}>
|
||||
<HowAboutThese
|
||||
relativeQueries={relativeQuerys}
|
||||
onQueryClick={handleFullQueryClick}
|
||||
onClose={handleCloseHowAboutThese}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{themeInfo && themeInfo?.length > 0 && (
|
||||
<div className={css.hotpicksSection} data-wheel-point="true">
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator} />
|
||||
<div className={css.sectionTitle}>Hot Picks ({themeInfo?.length})</div>
|
||||
</div>
|
||||
<div className={css.productList}>
|
||||
<TVirtualGridList
|
||||
dataSize={themeInfo?.length}
|
||||
direction="horizontal"
|
||||
renderItem={renderItem}
|
||||
itemWidth={416}
|
||||
itemHeight={436}
|
||||
spacing={20}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={css.itemBox}>
|
||||
<div className={css.tabContainer}>
|
||||
<TButtonTab
|
||||
contents={buttonTabList}
|
||||
onItemClick={handleButtonTabClick}
|
||||
selectedIndex={tab}
|
||||
listType={LIST_TYPE.medium}
|
||||
spotlightId={SpotlightIds.SEARCH_TAB_CONTAINER}
|
||||
/>
|
||||
{/* 2025/10/17 김영진 부장과 이야기 하여 일반에서는 아직 필터 검토하지않아 빼달라고 하여 우선 일반검색에서는 미노출 처리 추후 처리 필요함 */}
|
||||
{tab === 0 && !itemInfo?.length && (
|
||||
<TDropDown
|
||||
className={classNames(
|
||||
css.dropdown,
|
||||
styleChange === true ? css.categoryDropdown : null
|
||||
)}
|
||||
onSelect={handleSelectFilter}
|
||||
onOpen={handleStyle}
|
||||
onClose={handleStyleOut}
|
||||
selectedIndex={dropDownTab}
|
||||
width="small"
|
||||
>
|
||||
{filterMethods}
|
||||
</TDropDown>
|
||||
)}
|
||||
</div>
|
||||
{tab === 0 && <ItemCard itemInfo={displayedData} />}
|
||||
{tab === 1 && <ShowCard showInfo={displayedData} />}
|
||||
</div>
|
||||
<div className={css.buttonContainer}>
|
||||
{hasMore && (
|
||||
<SpottableDiv onClick={downBtnClick} className={css.downBtn}>
|
||||
<CustomImage className={css.btnImg} src={downBtnImg} alt="Down arrow" />
|
||||
</SpottableDiv>
|
||||
)}
|
||||
<SpottableDiv onClick={upBtnClick} className={css.upBtn}>
|
||||
<CustomImage className={css.btnImg} src={upBtnImg} alt="Up arrow" />
|
||||
</SpottableDiv>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchResultsNew;
|
||||
@@ -0,0 +1,362 @@
|
||||
@import "../../style/CommonStyle.module.less";
|
||||
@import "../../style/utils.module.less";
|
||||
|
||||
.searchBox {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative; // ⭐ 추가: absolute positioning을 위한 기준점
|
||||
.topBox {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 30px 60px;
|
||||
background: #DDDDDD;
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 19px;
|
||||
|
||||
.topBoxTitle {
|
||||
width: 254px;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
|
||||
.titleText {
|
||||
left: 53px;
|
||||
top: 10px;
|
||||
position: absolute;
|
||||
border-bottom: 1px black solid;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #272727;
|
||||
font-size: 24px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-weight: 700;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.topBoxList {
|
||||
flex: 1 1 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
|
||||
.topBoxListItem {
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 100px;
|
||||
outline: 1px #DADADA solid;
|
||||
outline-offset: -1px;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
color: black;
|
||||
font-size: 24px;
|
||||
font-family: 'LG Smart UI', sans-serif;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid #00a0e9;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f8f8f8;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.seeMoreButton {
|
||||
width: 200px;
|
||||
height: 60px;
|
||||
background: #808080;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid #00a0e9;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #666666;
|
||||
}
|
||||
|
||||
.seeMoreText {
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-family: 'LG Smart UI', sans-serif;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
.hotpicksSection {
|
||||
padding-top: 63px;
|
||||
padding-left: 60px;
|
||||
width: 1800px;
|
||||
height: 580px;
|
||||
.sectionHeader {
|
||||
width: 1800px;
|
||||
height: 42px;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
|
||||
> * {
|
||||
margin-right: 12px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sectionIndicator {
|
||||
width: 6px;
|
||||
height: 36px;
|
||||
background: #c70850;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
text-align: center;
|
||||
color: black;
|
||||
font-size: 42px;
|
||||
font-family: "LG Smart UI";
|
||||
font-weight: 700;
|
||||
line-height: 42px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
// 상품 리스트 스타일 (Hot Picks for You)
|
||||
.productList {
|
||||
padding-top: 30px;
|
||||
.size(@w: 100%, @h: inherit);
|
||||
> div:nth-child(1) {
|
||||
.size(@w: 100%, @h: inherit);
|
||||
}
|
||||
|
||||
.productCard {
|
||||
width: 546px;
|
||||
padding: 18px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
border: 5px solid #dadada;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: @PRIMARY_COLOR_RED;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 5px solid @PRIMARY_COLOR_RED;
|
||||
box-shadow: 0 0 22px 0 rgba(0, 0, 0, 0.5);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.productImageWrapper {
|
||||
align-self: stretch;
|
||||
height: 287px;
|
||||
position: relative;
|
||||
|
||||
.productImage {
|
||||
width: 510px;
|
||||
height: 287px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.productInfo {
|
||||
align-self: stretch;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
display: inline-flex;
|
||||
|
||||
> * {
|
||||
margin-right: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.productBrandWrapper {
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
> * {
|
||||
margin-right: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.brandLogo {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.productDetails {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
display: inline-flex;
|
||||
|
||||
> * {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.brandName {
|
||||
text-align: center;
|
||||
color: #808080;
|
||||
font-size: 18px;
|
||||
font-family: "LG Smart UI";
|
||||
font-weight: 700;
|
||||
line-height: 18px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.productTitle {
|
||||
align-self: stretch;
|
||||
color: black;
|
||||
font-size: 24px;
|
||||
font-family: "LG Smart UI";
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.itemBox {
|
||||
margin-top: 60px;
|
||||
width: 100%;
|
||||
padding-left: 60px;
|
||||
overflow: unset;
|
||||
.title {
|
||||
padding: 38px 0 33px 0;
|
||||
}
|
||||
|
||||
.tabContainer {
|
||||
width: -webkit-fit-content;
|
||||
height: auto;
|
||||
position: relative;
|
||||
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
&.categoryDropdown {
|
||||
> div {
|
||||
> div {
|
||||
border-radius: 6px 6px 0 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonContainer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 30px 0;
|
||||
.downBtn {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: #4f172c;
|
||||
margin-right: 10px;
|
||||
border-radius: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 4px solid #4f172c;
|
||||
.btnImg {
|
||||
width: 42px;
|
||||
}
|
||||
&:focus {
|
||||
border: 4px solid @PRIMARY_COLOR_RED;
|
||||
}
|
||||
}
|
||||
.upBtn {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: #4f172c;
|
||||
border-radius: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 4px solid #4f172c;
|
||||
.btnImg {
|
||||
width: 42px;
|
||||
}
|
||||
&:focus {
|
||||
border: 4px solid @PRIMARY_COLOR_RED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HowAboutThese Full 버전 오버레이
|
||||
.howAboutTheseOverlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 860px; // HowAboutThese의 높이
|
||||
z-index: 100;
|
||||
pointer-events: auto; // 오버레이 전체를 클릭 가능하게 (배경 클릭 방지)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user