[검색] 스타일 및 변경 작업#1
- Figma 기준 02-1 작업완료.
(관련 내용 주석으로 메모달아둠, 분리 필요)
- 02-2작업완료
(관련 내용 주석으로 메모달아둠, 분리 필요)
- 02-3 (카테고리 부분에대한 문의점으로 오현주 팀장님에게 문의넣어둠)
This commit is contained in:
BIN
com.twin.app.shoptime/assets/images/btn/btn_dropdown_wh.png
Normal file
BIN
com.twin.app.shoptime/assets/images/btn/btn_dropdown_wh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 579 B |
BIN
com.twin.app.shoptime/assets/images/ico_mic.png
Normal file
BIN
com.twin.app.shoptime/assets/images/ico_mic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
com.twin.app.shoptime/assets/images/searchpanel/image-mic.png
Normal file
BIN
com.twin.app.shoptime/assets/images/searchpanel/image-mic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
com.twin.app.shoptime/assets/images/searchpanel/img-hotpicks.png
Normal file
BIN
com.twin.app.shoptime/assets/images/searchpanel/img-hotpicks.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 705 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 411 KiB |
@@ -1,14 +1,17 @@
|
|||||||
import React, { useCallback, useState } from "react";
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import classNames from "classnames";
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { InputField } from "@enact/sandstone/Input";
|
import { InputField } from '@enact/sandstone/Input';
|
||||||
import Spotlight from "@enact/spotlight";
|
import Spotlight from '@enact/spotlight';
|
||||||
import Spottable from "@enact/spotlight/Spottable";
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
|
||||||
import useScrollReset from "../../hooks/useScrollReset";
|
import useScrollReset from '../../hooks/useScrollReset';
|
||||||
import TIconButton from "../TIconButton/TIconButton";
|
import TIconButton from '../TIconButton/TIconButton';
|
||||||
import css from "./TInput.module.less";
|
import css from './TInput.module.less';
|
||||||
|
|
||||||
const KINDS = { withIcon: "withIcon" };
|
const KINDS = { withIcon: "withIcon" };
|
||||||
const ICONS = { search: "search" };
|
const ICONS = { search: "search" };
|
||||||
@@ -30,6 +33,8 @@ export default function TInput({
|
|||||||
scrollTop,
|
scrollTop,
|
||||||
onIconClick,
|
onIconClick,
|
||||||
forcedSpotlight,
|
forcedSpotlight,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
const { handleScrollReset, handleStopScrolling } = useScrollReset(scrollTop);
|
const { handleScrollReset, handleStopScrolling } = useScrollReset(scrollTop);
|
||||||
@@ -66,6 +71,20 @@ export default function TInput({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
//focus,blur 추가
|
||||||
|
const _onFocus = useCallback(() => {
|
||||||
|
if (onFocus) {
|
||||||
|
onFocus();
|
||||||
|
}
|
||||||
|
handleScrollReset();
|
||||||
|
}, [onFocus]);
|
||||||
|
const _onBlur = useCallback(() => {
|
||||||
|
if (onBlur) {
|
||||||
|
onBlur();
|
||||||
|
}
|
||||||
|
handleStopScrolling();
|
||||||
|
}, [onBlur]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
className={classNames(css.container, className ? className : null)}
|
className={classNames(css.container, className ? className : null)}
|
||||||
@@ -78,8 +97,8 @@ export default function TInput({
|
|||||||
spotlightDisabled={spotlightDisabled}
|
spotlightDisabled={spotlightDisabled}
|
||||||
className={classNames(css.input)}
|
className={classNames(css.input)}
|
||||||
autoFocus
|
autoFocus
|
||||||
onFocus={handleScrollReset}
|
onFocus={_onFocus}
|
||||||
onBlur={handleStopScrolling}
|
onBlur={_onBlur}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
spotlightId={"input-field-box"}
|
spotlightId={"input-field-box"}
|
||||||
aria-label="Keyword edit box"
|
aria-label="Keyword edit box"
|
||||||
|
|||||||
@@ -5,49 +5,71 @@ import React, {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from 'react';
|
||||||
|
|
||||||
import micIcon from "../../../assets/images/searchpanel/image-mic.png";
|
import classNames from 'classnames';
|
||||||
import hotPicksImage from "../../../assets/images/searchpanel/img-hotpicks.png";
|
import {
|
||||||
import hotPicksBrandImage from "../../../assets/images/searchpanel/img-search-hotpicks.png";
|
useDispatch,
|
||||||
|
useSelector,
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
} from 'react-redux';
|
||||||
|
|
||||||
import { Job } from "@enact/core/util";
|
import { Job } from '@enact/core/util';
|
||||||
import Spotlight from "@enact/spotlight";
|
import Spotlight from '@enact/spotlight';
|
||||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
import SpotlightContainerDecorator
|
||||||
import Spottable from "@enact/spotlight/Spottable";
|
from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
import { setContainerLastFocusedElement } from "@enact/spotlight/src/container";
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
|
||||||
import { sendLogGNB, sendLogTotalRecommend } from "../../actions/logActions";
|
|
||||||
import { getMyRecommandedKeyword } from "../../actions/myPageActions";
|
import micIcon from '../../../assets/images/searchpanel/image-mic.png';
|
||||||
import { popPanel, updatePanel } from "../../actions/panelActions";
|
import hotPicksImage from '../../../assets/images/searchpanel/img-hotpicks.png';
|
||||||
import { getSearch, resetSearch } from "../../actions/searchActions";
|
import hotPicksBrandImage
|
||||||
|
from '../../../assets/images/searchpanel/img-search-hotpicks.png';
|
||||||
|
import {
|
||||||
|
sendLogGNB,
|
||||||
|
sendLogTotalRecommend,
|
||||||
|
} from '../../actions/logActions';
|
||||||
|
import { getMyRecommandedKeyword } from '../../actions/myPageActions';
|
||||||
|
import {
|
||||||
|
popPanel,
|
||||||
|
updatePanel,
|
||||||
|
} from '../../actions/panelActions';
|
||||||
|
import {
|
||||||
|
getSearch,
|
||||||
|
resetSearch,
|
||||||
|
} from '../../actions/searchActions';
|
||||||
import {
|
import {
|
||||||
showSuccessToast,
|
|
||||||
showErrorToast,
|
showErrorToast,
|
||||||
showSearchSuccessToast,
|
showInfoToast,
|
||||||
showSearchErrorToast,
|
showSearchErrorToast,
|
||||||
|
showSearchSuccessToast,
|
||||||
|
showSuccessToast,
|
||||||
showWarningToast,
|
showWarningToast,
|
||||||
showInfoToast
|
} from '../../actions/toastActions';
|
||||||
} from "../../actions/toastActions";
|
import TBody from '../../components/TBody/TBody';
|
||||||
import TBody from "../../components/TBody/TBody";
|
import TInput, {
|
||||||
import TInput, { ICONS, KINDS } from "../../components/TInput/TInput";
|
ICONS,
|
||||||
import TPanel from "../../components/TPanel/TPanel";
|
KINDS,
|
||||||
import TVerticalPagenator from "../../components/TVerticalPagenator/TVerticalPagenator";
|
} from '../../components/TInput/TInput';
|
||||||
import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
|
import TPanel from '../../components/TPanel/TPanel';
|
||||||
import usePrevious from "../../hooks/usePrevious";
|
import TScroller from '../../components/TScroller/TScroller';
|
||||||
|
import TVerticalPagenator
|
||||||
|
from '../../components/TVerticalPagenator/TVerticalPagenator';
|
||||||
|
import TVirtualGridList
|
||||||
|
from '../../components/TVirtualGridList/TVirtualGridList';
|
||||||
|
// import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
|
||||||
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import {
|
import {
|
||||||
LOG_CONTEXT_NAME,
|
LOG_CONTEXT_NAME,
|
||||||
LOG_MENU,
|
LOG_MENU,
|
||||||
LOG_MESSAGE_ID,
|
LOG_MESSAGE_ID,
|
||||||
panel_names,
|
panel_names,
|
||||||
} from "../../utils/Config";
|
} from '../../utils/Config';
|
||||||
import { SpotlightIds } from "../../utils/SpotlightIds";
|
import { SpotlightIds } from '../../utils/SpotlightIds';
|
||||||
import NoSearchResults from "./NoSearchResults/NoSearchResults";
|
import NoSearchResults from './NoSearchResults/NoSearchResults';
|
||||||
import RecommendedKeywords from "./RecommendedKeywords/RecommendedKeywords";
|
import RecommendedKeywords from './RecommendedKeywords/RecommendedKeywords';
|
||||||
import css from "./SearchPanel.new.module.less";
|
import css from './SearchPanel.new.module.less';
|
||||||
import SearchResults from "./SearchResults/SearchResults";
|
import SearchResultsNew from './SearchResults.new';
|
||||||
|
import SearchResults from './SearchResults/SearchResults';
|
||||||
|
|
||||||
const ContainerBasic = SpotlightContainerDecorator(
|
const ContainerBasic = SpotlightContainerDecorator(
|
||||||
{ enterTo: "last-focused" },
|
{ enterTo: "last-focused" },
|
||||||
@@ -82,10 +104,15 @@ const SPOTLIGHT_IDS = {
|
|||||||
TOP_SEARCHES_SECTION: "top-searches-section",
|
TOP_SEARCHES_SECTION: "top-searches-section",
|
||||||
POPULAR_BRANDS_SECTION: "popular-brands-section",
|
POPULAR_BRANDS_SECTION: "popular-brands-section",
|
||||||
HOT_PICKS_SECTION: "hot-picks-section",
|
HOT_PICKS_SECTION: "hot-picks-section",
|
||||||
SEARCH_VERTICAL_PAGENATOR: "search_verticalPagenator"
|
SEARCH_VERTICAL_PAGENATOR: "search_verticalPagenator",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
export default function SearchPanel({
|
||||||
|
panelInfo,
|
||||||
|
isOnTop,
|
||||||
|
spotlightId,
|
||||||
|
scrollOptions = [],
|
||||||
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const loadingComplete = useSelector((state) => state.common?.loadingComplete);
|
const loadingComplete = useSelector((state) => state.common?.loadingComplete);
|
||||||
const recommandedKeywords = useSelector(
|
const recommandedKeywords = useSelector(
|
||||||
@@ -105,6 +132,18 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
const [position, setPosition] = useState(null);
|
const [position, setPosition] = useState(null);
|
||||||
const [showVirtualKeyboard, setShowVirtualKeyboard] = useState(false);
|
const [showVirtualKeyboard, setShowVirtualKeyboard] = useState(false);
|
||||||
|
|
||||||
|
//인풋창 포커스 구분을 위함
|
||||||
|
const [inputFocus, setInputFocus] = useState(false);
|
||||||
|
const _onFocus = () => {
|
||||||
|
setInputFocus(true);
|
||||||
|
};
|
||||||
|
const _onBlur = () => {
|
||||||
|
setInputFocus(false);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("###inputFocus", inputFocus);
|
||||||
|
}, [inputFocus]);
|
||||||
|
|
||||||
let searchQueryRef = usePrevious(searchQuery);
|
let searchQueryRef = usePrevious(searchQuery);
|
||||||
let isOnTopRef = usePrevious(isOnTop);
|
let isOnTopRef = usePrevious(isOnTop);
|
||||||
|
|
||||||
@@ -122,17 +161,34 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 가짜 데이터 - 실제로는 Redux store나 API에서 가져와야 함
|
// 가짜 데이터 - 실제로는 Redux store나 API에서 가져와야 함
|
||||||
const recentSearches = useMemo(() => ["Puppy food", "Dog toy", "Fitness"], []);
|
const recentSearches = useMemo(
|
||||||
const topSearches = useMemo(() => ["Mather's Day", "Gift", "Easter Day", "Royal Canin puppy food", "Fitness", "Parrot"], []);
|
() => ["Puppy food", "Dog toy", "Fitness"],
|
||||||
const popularBrands = useMemo(() => ["Shark", "Ninja", "Skechers", "LocknLock", "8Greens", "LGE"], []);
|
[]
|
||||||
const hotPicks = useMemo(() => [
|
);
|
||||||
|
const topSearches = useMemo(
|
||||||
|
() => [
|
||||||
|
"Mather's Day",
|
||||||
|
"Gift",
|
||||||
|
"Easter Day",
|
||||||
|
"Royal Canin puppy food",
|
||||||
|
"Fitness",
|
||||||
|
"Parrot",
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const popularBrands = useMemo(
|
||||||
|
() => ["Shark", "Ninja", "Skechers", "LocknLock", "8Greens", "LGE"],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const hotPicks = useMemo(
|
||||||
|
() => [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
image: hotPicksImage,
|
image: hotPicksImage,
|
||||||
brandLogo: hotPicksBrandImage,
|
brandLogo: hotPicksBrandImage,
|
||||||
brandName: "Product Name",
|
brandName: "Product Name",
|
||||||
title: "New Shark Vacuum! Your pet Hair Solution!",
|
title: "New Shark Vacuum! Your pet Hair Solution!",
|
||||||
isForYou: false
|
isForYou: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
@@ -140,7 +196,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
brandLogo: hotPicksBrandImage,
|
brandLogo: hotPicksBrandImage,
|
||||||
brandName: "Product Name",
|
brandName: "Product Name",
|
||||||
title: "New Shark Vacuum! Your pet Hair Solution!",
|
title: "New Shark Vacuum! Your pet Hair Solution!",
|
||||||
isForYou: false
|
isForYou: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
@@ -148,7 +204,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
brandLogo: hotPicksBrandImage,
|
brandLogo: hotPicksBrandImage,
|
||||||
brandName: "Product Name",
|
brandName: "Product Name",
|
||||||
title: "New Shark Vacuum! Your pet Hair Solution!",
|
title: "New Shark Vacuum! Your pet Hair Solution!",
|
||||||
isForYou: false
|
isForYou: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
@@ -156,9 +212,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
brandLogo: hotPicksBrandImage,
|
brandLogo: hotPicksBrandImage,
|
||||||
brandName: "Product Name",
|
brandName: "Product Name",
|
||||||
title: "New Shark Vacuum! Your pet Hair Solution!",
|
title: "New Shark Vacuum! Your pet Hair Solution!",
|
||||||
isForYou: true
|
isForYou: true,
|
||||||
}
|
},
|
||||||
], []);
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loadingComplete && !recommandedKeywords) {
|
if (loadingComplete && !recommandedKeywords) {
|
||||||
@@ -305,7 +363,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleKeydown = useCallback((e) => {
|
const handleKeydown = useCallback(
|
||||||
|
(e) => {
|
||||||
if (!isOnTopRef.current) {
|
if (!isOnTopRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -324,10 +383,23 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 방향키 처리 - Spotlight 네비게이션 허용
|
// 방향키 처리 - Spotlight 네비게이션 허용
|
||||||
const arrowKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Left", "Right", "Up", "Down"];
|
const arrowKeys = [
|
||||||
|
"ArrowLeft",
|
||||||
|
"ArrowRight",
|
||||||
|
"ArrowUp",
|
||||||
|
"ArrowDown",
|
||||||
|
"Left",
|
||||||
|
"Right",
|
||||||
|
"Up",
|
||||||
|
"Down",
|
||||||
|
];
|
||||||
if (arrowKeys.includes(e.key)) {
|
if (arrowKeys.includes(e.key)) {
|
||||||
// 입력 필드가 비어있고 왼쪽 화살표인 경우에만 방지
|
// 입력 필드가 비어있고 왼쪽 화살표인 경우에만 방지
|
||||||
if (position === 0 && (e.key === "Left" || e.key === "ArrowLeft") && !searchQuery) {
|
if (
|
||||||
|
position === 0 &&
|
||||||
|
(e.key === "Left" || e.key === "ArrowLeft") &&
|
||||||
|
!searchQuery
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -335,7 +407,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 오른쪽 화살표 키 처리 - 포커스 이동 허용
|
// 오른쪽 화살표 키 처리 - 포커스 이동 허용
|
||||||
if (e.key === "ArrowRight" || e.key === "Right") {
|
if (e.key === "ArrowRight" || e.key === "Right") {
|
||||||
// 커서가 텍스트 끝에 있을 때만 포커스 이동 허용
|
// 커서가 텍스트 끝에 있을 때만 포커스 이동 허용
|
||||||
const input = document.querySelector(`[data-spotlight-id="input-field-box"] > input`);
|
const input = document.querySelector(
|
||||||
|
`[data-spotlight-id="input-field-box"] > input`
|
||||||
|
);
|
||||||
if (input && position === input.value.length) {
|
if (input && position === input.value.length) {
|
||||||
// 커서가 텍스트 끝에 있으면 포커스 이동 허용
|
// 커서가 텍스트 끝에 있으면 포커스 이동 허용
|
||||||
return;
|
return;
|
||||||
@@ -345,7 +419,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
// 나머지 방향키는 Spotlight가 처리하도록 허용
|
// 나머지 방향키는 Spotlight가 처리하도록 허용
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}, [searchQuery, position, handleSearchSubmit, showVirtualKeyboard]);
|
},
|
||||||
|
[searchQuery, position, handleSearchSubmit, showVirtualKeyboard]
|
||||||
|
);
|
||||||
|
|
||||||
const cursorPosition = useCallback(() => {
|
const cursorPosition = useCallback(() => {
|
||||||
const input = document.querySelector(
|
const input = document.querySelector(
|
||||||
@@ -397,12 +473,19 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
}, [panelInfo, firstSpot]);
|
}, [panelInfo, firstSpot]);
|
||||||
|
|
||||||
// 키워드 클릭 핸들러
|
// 키워드 클릭 핸들러
|
||||||
const handleKeywordClick = useCallback((keyword) => {
|
const handleKeywordClick = useCallback(
|
||||||
|
(keyword) => {
|
||||||
setSearchQuery(keyword);
|
setSearchQuery(keyword);
|
||||||
handleSearchSubmit(keyword);
|
handleSearchSubmit(keyword);
|
||||||
// 키워드 선택 알림
|
// 키워드 선택 알림
|
||||||
dispatch(showSuccessToast(`"${keyword}" 키워드로 검색합니다.`, { duration: 2000 }));
|
dispatch(
|
||||||
}, [handleSearchSubmit, dispatch]);
|
showSuccessToast(`"${keyword}" 키워드로 검색합니다.`, {
|
||||||
|
duration: 2000,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[handleSearchSubmit, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
// 상품 클릭 핸들러
|
// 상품 클릭 핸들러
|
||||||
const handleProductClick = useCallback((product) => {
|
const handleProductClick = useCallback((product) => {
|
||||||
@@ -417,58 +500,62 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
// ProductCard 컴포넌트
|
// ProductCard 컴포넌트
|
||||||
const ProductCard = useCallback(({
|
const renderItem = useCallback(
|
||||||
product,
|
(
|
||||||
index,
|
// {
|
||||||
onClick,
|
// product,
|
||||||
showForYouBadge = true,
|
// index,
|
||||||
|
// onClick,
|
||||||
|
// showBrandLogo = true,
|
||||||
|
// showBrandName = true,
|
||||||
|
// showProductTitle = true,
|
||||||
|
// ...rest
|
||||||
|
// }
|
||||||
|
{ index, ...rest }
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
showBrandLogo = true,
|
showBrandLogo = true,
|
||||||
showBrandName = true,
|
showBrandName = true,
|
||||||
showProductTitle = true,
|
showProductTitle = true,
|
||||||
badgeText = "For You",
|
image,
|
||||||
badgePosition = "top-right"
|
title,
|
||||||
}) => {
|
brandLogo,
|
||||||
|
brandName,
|
||||||
|
} = hotPicks[index];
|
||||||
return (
|
return (
|
||||||
<SpottableProduct
|
<SpottableProduct
|
||||||
key={`product-${index}`}
|
key={`product-${index}`}
|
||||||
className={css.productCard}
|
className={css.productCard}
|
||||||
onClick={() => onClick?.(product)}
|
|
||||||
spotlightId={`product-${index}`}
|
spotlightId={`product-${index}`}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<div className={css.productImageWrapper}>
|
<div className={css.productImageWrapper}>
|
||||||
<img
|
<img src={image} alt={title} className={css.productImage} />
|
||||||
src={product.image}
|
|
||||||
alt={product.title}
|
|
||||||
className={css.productImage}
|
|
||||||
/>
|
|
||||||
{showForYouBadge && product.isForYou && (
|
|
||||||
<div className={`${css.forYouBadge} ${css[badgePosition]}`}>
|
|
||||||
{badgeText}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className={css.productInfo}>
|
<div className={css.productInfo}>
|
||||||
{showBrandLogo && (
|
{showBrandLogo && (
|
||||||
<div className={css.productBrandWrapper}>
|
<div className={css.productBrandWrapper}>
|
||||||
<img
|
<img
|
||||||
src={product.brandLogo}
|
src={brandLogo}
|
||||||
alt={product.brandName}
|
alt={brandName}
|
||||||
className={css.brandLogo}
|
className={css.brandLogo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={css.productDetails}>
|
<div className={css.productDetails}>
|
||||||
{showBrandName && (
|
{showBrandName && (
|
||||||
<div className={css.brandName}>{product.brandName}</div>
|
<div className={css.brandName}>{brandName}</div>
|
||||||
)}
|
)}
|
||||||
{showProductTitle && (
|
{showProductTitle && (
|
||||||
<div className={css.productTitle}>{product.title}</div>
|
<div className={css.productTitle}>{title}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SpottableProduct>
|
</SpottableProduct>
|
||||||
);
|
);
|
||||||
}, []);
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TPanel
|
<TPanel
|
||||||
@@ -495,7 +582,11 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
>
|
>
|
||||||
{/* 검색 입력 영역 */}
|
{/* 검색 입력 영역 */}
|
||||||
<InputContainer
|
<InputContainer
|
||||||
className={css.inputContainer}
|
className={classNames(
|
||||||
|
css.inputContainer,
|
||||||
|
inputFocus === true && css.inputFocus,
|
||||||
|
css.searchValue /* 이건 결과값 있을때만. 조건 추가필요 */
|
||||||
|
)}
|
||||||
data-wheel-point={true}
|
data-wheel-point={true}
|
||||||
spotlightId={SPOTLIGHT_IDS.SEARCH_INPUT_LAYER}
|
spotlightId={SPOTLIGHT_IDS.SEARCH_INPUT_LAYER}
|
||||||
>
|
>
|
||||||
@@ -520,6 +611,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
spotlightDisabled={false}
|
spotlightDisabled={false}
|
||||||
spotlightBoxDisabled={true}
|
spotlightBoxDisabled={true}
|
||||||
|
onFocus={_onFocus}
|
||||||
|
onBlur={_onBlur}
|
||||||
/>
|
/>
|
||||||
<SpottableMicButton
|
<SpottableMicButton
|
||||||
className={css.microphoneButton}
|
className={css.microphoneButton}
|
||||||
@@ -536,19 +629,19 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
</SpottableMicButton>
|
</SpottableMicButton>
|
||||||
|
|
||||||
{/* 테스트용 Toast 버튼 (개발용) */}
|
{/* 테스트용 Toast 버튼 (개발용) */}
|
||||||
<SpottableMicButton
|
{/* <SpottableMicButton
|
||||||
className={css.testToastButton}
|
className={css.testToastButton}
|
||||||
onClick={handleTestToasts}
|
onClick={handleTestToasts}
|
||||||
spotlightId="test-toast-button"
|
spotlightId="test-toast-button"
|
||||||
>
|
>
|
||||||
<div className={css.testButtonCircle}>
|
<div className={css.testButtonCircle}>🧪</div>
|
||||||
🧪
|
</SpottableMicButton> */}
|
||||||
</div>
|
|
||||||
</SpottableMicButton>
|
|
||||||
</div>
|
</div>
|
||||||
</InputContainer>
|
</InputContainer>
|
||||||
|
{/* 결과갑 부분 작업중 */}
|
||||||
{/* 검색 결과 표시 영역 */}
|
<SearchResultsNew />
|
||||||
|
{/* //결과갑 부분 작업중 */}
|
||||||
|
{/* 검색 결과 표시 영역
|
||||||
{searchPerformed && searchQuery !== null ? (
|
{searchPerformed && searchQuery !== null ? (
|
||||||
Object.keys(searchDatas).length > 0 ? (
|
Object.keys(searchDatas).length > 0 ? (
|
||||||
<SearchResults
|
<SearchResults
|
||||||
@@ -561,7 +654,10 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<ContainerBasic className={css.contentContainer}>
|
<ContainerBasic className={css.contentContainer}>
|
||||||
{/* 최근 검색어 섹션 */}
|
{/* 노출 조건 변경 필요. 포커스 블러만으로는 안됌.(가상 키보드 노출시가 맞을듯)
|
||||||
|
{inputFocus === false ? (
|
||||||
|
<>
|
||||||
|
{/* 최근 검색어 섹션
|
||||||
<SectionContainer
|
<SectionContainer
|
||||||
className={css.section}
|
className={css.section}
|
||||||
data-wheel-point={true}
|
data-wheel-point={true}
|
||||||
@@ -569,7 +665,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
>
|
>
|
||||||
<div className={css.sectionHeader}>
|
<div className={css.sectionHeader}>
|
||||||
<div className={css.sectionIndicator}></div>
|
<div className={css.sectionIndicator}></div>
|
||||||
<div className={css.sectionTitle}>Your Recent Searches</div>
|
<div className={css.sectionTitle}>
|
||||||
|
Your Recent Searches
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={css.keywordList}>
|
<div className={css.keywordList}>
|
||||||
{recentSearches.map((keyword, index) => (
|
{recentSearches.map((keyword, index) => (
|
||||||
@@ -585,7 +683,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
</div>
|
</div>
|
||||||
</SectionContainer>
|
</SectionContainer>
|
||||||
|
|
||||||
{/* 인기 검색어 섹션 */}
|
{/* 인기 검색어 섹션
|
||||||
<SectionContainer
|
<SectionContainer
|
||||||
className={css.section}
|
className={css.section}
|
||||||
data-wheel-point={true}
|
data-wheel-point={true}
|
||||||
@@ -609,7 +707,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
</div>
|
</div>
|
||||||
</SectionContainer>
|
</SectionContainer>
|
||||||
|
|
||||||
{/* 인기 브랜드 섹션 */}
|
{/* 인기 브랜드 섹션
|
||||||
<SectionContainer
|
<SectionContainer
|
||||||
className={css.section}
|
className={css.section}
|
||||||
data-wheel-point={true}
|
data-wheel-point={true}
|
||||||
@@ -633,45 +731,62 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
|||||||
</div>
|
</div>
|
||||||
</SectionContainer>
|
</SectionContainer>
|
||||||
|
|
||||||
{/* Hot Picks for You 섹션 */}
|
{/* Hot Picks for You 섹션
|
||||||
<SectionContainer
|
<SectionContainer
|
||||||
className={css.section}
|
className={css.hotpicksSection}
|
||||||
data-wheel-point={true}
|
data-wheel-point={true}
|
||||||
spotlightId={SPOTLIGHT_IDS.HOT_PICKS_SECTION}
|
spotlightId={SPOTLIGHT_IDS.HOT_PICKS_SECTION}
|
||||||
>
|
>
|
||||||
<div className={css.sectionHeader}>
|
<div className={css.sectionHeader}>
|
||||||
<div className={css.sectionIndicator}></div>
|
<div className={css.sectionIndicator}></div>
|
||||||
<div className={css.sectionTitle}>Hot Picks for You</div>
|
<div className={css.sectionTitle}>
|
||||||
|
Hot Picks for You
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={css.productList}>
|
<div className={css.productList}>
|
||||||
{hotPicks.map((product, index) => (
|
{hotPicks && hotPicks.length > 0 && (
|
||||||
<ProductCard
|
<TVirtualGridList
|
||||||
key={`product-${index}`}
|
dataSize={hotPicks.length}
|
||||||
product={product}
|
direction="horizontal"
|
||||||
index={index}
|
renderItem={renderItem}
|
||||||
onClick={handleProductClick}
|
// itemWidth={546}
|
||||||
showForYouBadge={true}
|
itemWidth={416}
|
||||||
showBrandLogo={true}
|
itemHeight={436}
|
||||||
showBrandName={true}
|
spacing={20}
|
||||||
showProductTitle={true}
|
|
||||||
badgeText="For You"
|
|
||||||
badgePosition="top-right"
|
|
||||||
/>
|
/>
|
||||||
))}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</SectionContainer>
|
</SectionContainer>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className={css.inputFocusBox}>
|
||||||
|
<div className={css.keywordList}>
|
||||||
|
{recentSearches.map((keyword, index) => (
|
||||||
|
<SpottableKeyword
|
||||||
|
key={`recent-${index}`}
|
||||||
|
className={css.keywordButton}
|
||||||
|
onClick={() => handleKeywordClick(keyword)}
|
||||||
|
spotlightId={`recent-keyword-${index}`}
|
||||||
|
>
|
||||||
|
{keyword}
|
||||||
|
</SpottableKeyword>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</ContainerBasic>
|
</ContainerBasic>
|
||||||
)}
|
)}
|
||||||
|
*/}
|
||||||
</TVerticalPagenator>
|
</TVerticalPagenator>
|
||||||
)}
|
)}
|
||||||
</ContainerBasic>
|
</ContainerBasic>
|
||||||
</TBody>
|
</TBody>
|
||||||
|
|
||||||
{/* Virtual Keyboard */}
|
{/* Virtual Keyboard */}
|
||||||
<VirtualKeyboardContainer
|
{/* <VirtualKeyboardContainer
|
||||||
isVisible={showVirtualKeyboard}
|
isVisible={showVirtualKeyboard}
|
||||||
onClose={() => setShowVirtualKeyboard(false)}
|
onClose={() => setShowVirtualKeyboard(false)}
|
||||||
/>
|
/> */}
|
||||||
</TPanel>
|
</TPanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,13 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
&.inputFocus {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
&.searchValue {
|
||||||
|
padding-bottom: 55px;
|
||||||
|
padding-top: 55px;
|
||||||
|
}
|
||||||
> * {
|
> * {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
@@ -73,10 +79,10 @@
|
|||||||
background-color: #fff !important;
|
background-color: #fff !important;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
text-align: center;
|
text-align: left;
|
||||||
color: black;
|
color: black;
|
||||||
font-size: 42px;
|
font-size: 42px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 42px;
|
line-height: 42px;
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -166,13 +172,13 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: "";
|
||||||
width: 36.27px;
|
width: 36.27px;
|
||||||
height: 36.27px;
|
height: 36.27px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 1.95px;
|
left: 1.95px;
|
||||||
top: 1.95px;
|
top: 1.95px;
|
||||||
border: 3.90px solid black;
|
border: 3.9px solid black;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,10 +208,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.microphoneIcon {
|
.microphoneIcon {
|
||||||
width: 100%;
|
height: 50px;
|
||||||
height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
padding: 10px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,6 +280,64 @@
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
.inputFocusBox {
|
||||||
|
width: 995px;
|
||||||
|
height: 355px;
|
||||||
|
margin: 0 auto;
|
||||||
|
.keywordList {
|
||||||
|
align-self: stretch;
|
||||||
|
padding-top: 10px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-start;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
> * {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keywordButton {
|
||||||
|
padding: 20px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 100px;
|
||||||
|
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;
|
||||||
|
height: 64px;
|
||||||
|
> * {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
color: black;
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: "LG Smart UI";
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 24px;
|
||||||
|
text-align: center;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: @PRIMARY_COLOR_RED;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border: 5px solid @PRIMARY_COLOR_RED;
|
||||||
|
box-shadow: 0 0 22px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 섹션 공통 스타일
|
// 섹션 공통 스타일
|
||||||
@@ -315,14 +376,14 @@
|
|||||||
.sectionIndicator {
|
.sectionIndicator {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
background: #C70850;
|
background: #c70850;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sectionTitle {
|
.sectionTitle {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: black;
|
color: black;
|
||||||
font-size: 42px;
|
font-size: 42px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 42px;
|
line-height: 42px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -341,17 +402,13 @@
|
|||||||
> * {
|
> * {
|
||||||
margin-right: 19px;
|
margin-right: 19px;
|
||||||
margin-bottom: 19px;
|
margin-bottom: 19px;
|
||||||
|
|
||||||
&:nth-child(3n) {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.keywordButton {
|
.keywordButton {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
border: 5px solid #DADADA;
|
border: 5px solid #dadada;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -369,7 +426,7 @@
|
|||||||
|
|
||||||
color: black;
|
color: black;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -388,50 +445,68 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 상품 리스트 스타일 (Hot Picks for You)
|
.hotpicksSection {
|
||||||
.productList {
|
padding-top: 63px;
|
||||||
align-self: stretch;
|
padding-left: 60px;
|
||||||
padding-top: 30px;
|
width: 1800px;
|
||||||
|
height: 580px;
|
||||||
|
.sectionHeader {
|
||||||
|
width: 1800px;
|
||||||
|
height: 42px;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
margin-right: 19px;
|
margin-right: 12px;
|
||||||
margin-bottom: 19px;
|
|
||||||
|
|
||||||
&:nth-child(2n) {
|
&:last-child {
|
||||||
margin-right: 0;
|
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 {
|
.productCard {
|
||||||
width: 546px;
|
width: 546px;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
border: 5px solid #DADADA;
|
border: 5px solid #dadada;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
||||||
> * {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: @PRIMARY_COLOR_RED;
|
border-color: @PRIMARY_COLOR_RED;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
@@ -454,51 +529,6 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.forYouBadge {
|
|
||||||
height: 29.33px;
|
|
||||||
padding-left: 5.33px;
|
|
||||||
padding-right: 5.33px;
|
|
||||||
position: absolute;
|
|
||||||
background: #C70850;
|
|
||||||
border-radius: 5.33px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: white;
|
|
||||||
font-size: 21.33px;
|
|
||||||
font-family: 'LG Smart UI';
|
|
||||||
font-weight: 700;
|
|
||||||
word-wrap: break-word;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
// 위치별 스타일
|
|
||||||
&.top-right {
|
|
||||||
right: 10px;
|
|
||||||
top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.top-left {
|
|
||||||
left: 10px;
|
|
||||||
top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom-right {
|
|
||||||
right: 10px;
|
|
||||||
bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom-left {
|
|
||||||
left: 10px;
|
|
||||||
bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.center {
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.productInfo {
|
.productInfo {
|
||||||
@@ -557,7 +587,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
color: #808080;
|
color: #808080;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -567,7 +597,7 @@
|
|||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
color: black;
|
color: black;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -606,7 +636,6 @@
|
|||||||
[data-spotlight-id] {
|
[data-spotlight-id] {
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: 2px solid @PRIMARY_COLOR_RED;
|
outline: 2px solid @PRIMARY_COLOR_RED;
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
import SpotlightContainerDecorator
|
||||||
|
from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
|
||||||
|
import hotPicksImage from '../../../assets/images/searchpanel/img-hotpicks.png';
|
||||||
|
import hotPicksBrandImage
|
||||||
|
from '../../../assets/images/searchpanel/img-search-hotpicks.png';
|
||||||
|
import TVirtualGridList
|
||||||
|
from '../../components/TVirtualGridList/TVirtualGridList';
|
||||||
|
import css from './SearchResults.new.module.less';
|
||||||
|
|
||||||
|
const SearchResultsNew = () => {
|
||||||
|
const Container = SpotlightContainerDecorator("div");
|
||||||
|
const SectionContainer = SpotlightContainerDecorator("div");
|
||||||
|
const SpottableLi = Spottable("li");
|
||||||
|
const SpottableDiv = Spottable("div");
|
||||||
|
const hotPicks = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
image: hotPicksImage,
|
||||||
|
brandLogo: hotPicksBrandImage,
|
||||||
|
brandName: "Product Name",
|
||||||
|
title: "New Shark Vacuum! Your pet Hair Solution!",
|
||||||
|
isForYou: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
image: hotPicksImage,
|
||||||
|
brandLogo: hotPicksBrandImage,
|
||||||
|
brandName: "Product Name",
|
||||||
|
title: "New Shark Vacuum! Your pet Hair Solution!",
|
||||||
|
isForYou: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
image: hotPicksImage,
|
||||||
|
brandLogo: hotPicksBrandImage,
|
||||||
|
brandName: "Product Name",
|
||||||
|
title: "New Shark Vacuum! Your pet Hair Solution!",
|
||||||
|
isForYou: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
image: hotPicksImage,
|
||||||
|
brandLogo: hotPicksBrandImage,
|
||||||
|
brandName: "Product Name",
|
||||||
|
title: "New Shark Vacuum! Your pet Hair Solution!",
|
||||||
|
isForYou: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
// ProductCard 컴포넌트
|
||||||
|
const renderItem = useCallback(({ index, ...rest }) => {
|
||||||
|
const {
|
||||||
|
showBrandLogo = true,
|
||||||
|
showBrandName = true,
|
||||||
|
showProductTitle = true,
|
||||||
|
image,
|
||||||
|
title,
|
||||||
|
brandLogo,
|
||||||
|
brandName,
|
||||||
|
} = hotPicks[index];
|
||||||
|
return (
|
||||||
|
<SpottableDiv
|
||||||
|
key={`product-${index}`}
|
||||||
|
className={css.productCard}
|
||||||
|
spotlightId={`product-${index}`}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
<div className={css.productImageWrapper}>
|
||||||
|
<img src={image} alt={title} className={css.productImage} />
|
||||||
|
</div>
|
||||||
|
<div className={css.productInfo}>
|
||||||
|
{showBrandLogo && (
|
||||||
|
<div className={css.productBrandWrapper}>
|
||||||
|
<img src={brandLogo} alt={brandName} className={css.brandLogo} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={css.productDetails}>
|
||||||
|
{showBrandName && <div className={css.brandName}>{brandName}</div>}
|
||||||
|
{showProductTitle && (
|
||||||
|
<div className={css.productTitle}>{title}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SpottableDiv>
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<Container className={css.searchBox}>
|
||||||
|
<SectionContainer className={css.topBox}>
|
||||||
|
<span className={css.topBoxTitle}>How about these?</span>
|
||||||
|
<ul className={css.topBoxList}>
|
||||||
|
<SpottableLi className={css.topBoxListItem}>Puppy food</SpottableLi>
|
||||||
|
<SpottableLi className={css.topBoxListItem}>Dog toy</SpottableLi>
|
||||||
|
<SpottableLi className={css.topBoxListItem}>Fitness</SpottableLi>
|
||||||
|
</ul>
|
||||||
|
</SectionContainer>
|
||||||
|
<SectionContainer
|
||||||
|
className={css.hotpicksSection}
|
||||||
|
data-wheel-point={true}
|
||||||
|
spotlightId={"hot-picks-section"}
|
||||||
|
>
|
||||||
|
<div className={css.sectionHeader}>
|
||||||
|
<div className={css.sectionIndicator}></div>
|
||||||
|
<div className={css.sectionTitle}>Hot Picks (#)</div>
|
||||||
|
</div>
|
||||||
|
<div className={css.productList}>
|
||||||
|
{hotPicks && hotPicks.length > 0 && (
|
||||||
|
<TVirtualGridList
|
||||||
|
dataSize={hotPicks.length}
|
||||||
|
direction="horizontal"
|
||||||
|
renderItem={renderItem}
|
||||||
|
itemWidth={416}
|
||||||
|
itemHeight={436}
|
||||||
|
spacing={20}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SectionContainer>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SearchResultsNew;
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
@import "../../style/CommonStyle.module.less";
|
||||||
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
|
.searchBox {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.topBox {
|
||||||
|
width: 100%;
|
||||||
|
height: 124px;
|
||||||
|
background-color: #ddd;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.topBoxTitle {
|
||||||
|
margin-left: 60px;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #272727;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.topBoxList {
|
||||||
|
margin-left: 20px;
|
||||||
|
display: flex;
|
||||||
|
.topBoxListItem {
|
||||||
|
margin-right: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 64px;
|
||||||
|
padding: 0 20px;
|
||||||
|
background-color: @COLOR_WHITE;
|
||||||
|
border-radius: 32px;
|
||||||
|
color: #000;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 24px;
|
||||||
|
border: 4px solid @COLOR_WHITE;
|
||||||
|
&:focus {
|
||||||
|
border: 4px solid @PRIMARY_COLOR_RED;
|
||||||
|
box-shadow: 0 0 22px 0 rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user