[검색] 스타일 및 변경 작업#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 Spotlight from "@enact/spotlight";
|
||||
import Spottable from "@enact/spotlight/Spottable";
|
||||
import { InputField } from '@enact/sandstone/Input';
|
||||
import Spotlight from '@enact/spotlight';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
|
||||
import useScrollReset from "../../hooks/useScrollReset";
|
||||
import TIconButton from "../TIconButton/TIconButton";
|
||||
import css from "./TInput.module.less";
|
||||
import useScrollReset from '../../hooks/useScrollReset';
|
||||
import TIconButton from '../TIconButton/TIconButton';
|
||||
import css from './TInput.module.less';
|
||||
|
||||
const KINDS = { withIcon: "withIcon" };
|
||||
const ICONS = { search: "search" };
|
||||
@@ -30,6 +33,8 @@ export default function TInput({
|
||||
scrollTop,
|
||||
onIconClick,
|
||||
forcedSpotlight,
|
||||
onFocus,
|
||||
onBlur,
|
||||
...rest
|
||||
}) {
|
||||
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 (
|
||||
<Container
|
||||
className={classNames(css.container, className ? className : null)}
|
||||
@@ -78,8 +97,8 @@ export default function TInput({
|
||||
spotlightDisabled={spotlightDisabled}
|
||||
className={classNames(css.input)}
|
||||
autoFocus
|
||||
onFocus={handleScrollReset}
|
||||
onBlur={handleStopScrolling}
|
||||
onFocus={_onFocus}
|
||||
onBlur={_onBlur}
|
||||
onKeyDown={onKeyDown}
|
||||
spotlightId={"input-field-box"}
|
||||
aria-label="Keyword edit box"
|
||||
|
||||
@@ -5,49 +5,71 @@ import React, {
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
} from 'react';
|
||||
|
||||
import micIcon from "../../../assets/images/searchpanel/image-mic.png";
|
||||
import hotPicksImage from "../../../assets/images/searchpanel/img-hotpicks.png";
|
||||
import hotPicksBrandImage from "../../../assets/images/searchpanel/img-search-hotpicks.png";
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
useDispatch,
|
||||
useSelector,
|
||||
} from 'react-redux';
|
||||
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Job } from '@enact/core/util';
|
||||
import Spotlight from '@enact/spotlight';
|
||||
import SpotlightContainerDecorator
|
||||
from '@enact/spotlight/SpotlightContainerDecorator';
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
|
||||
|
||||
import { Job } from "@enact/core/util";
|
||||
import Spotlight from "@enact/spotlight";
|
||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||
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 { popPanel, updatePanel } from "../../actions/panelActions";
|
||||
import { getSearch, resetSearch } from "../../actions/searchActions";
|
||||
import {
|
||||
showSuccessToast,
|
||||
showErrorToast,
|
||||
showSearchSuccessToast,
|
||||
import micIcon from '../../../assets/images/searchpanel/image-mic.png';
|
||||
import hotPicksImage from '../../../assets/images/searchpanel/img-hotpicks.png';
|
||||
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 {
|
||||
showErrorToast,
|
||||
showInfoToast,
|
||||
showSearchErrorToast,
|
||||
showSearchSuccessToast,
|
||||
showSuccessToast,
|
||||
showWarningToast,
|
||||
showInfoToast
|
||||
} from "../../actions/toastActions";
|
||||
import TBody from "../../components/TBody/TBody";
|
||||
import TInput, { ICONS, KINDS } from "../../components/TInput/TInput";
|
||||
import TPanel from "../../components/TPanel/TPanel";
|
||||
import TVerticalPagenator from "../../components/TVerticalPagenator/TVerticalPagenator";
|
||||
import VirtualKeyboardContainer from "../../components/TToast/VirtualKeyboardContainer";
|
||||
import usePrevious from "../../hooks/usePrevious";
|
||||
} from '../../actions/toastActions';
|
||||
import TBody from '../../components/TBody/TBody';
|
||||
import TInput, {
|
||||
ICONS,
|
||||
KINDS,
|
||||
} from '../../components/TInput/TInput';
|
||||
import TPanel from '../../components/TPanel/TPanel';
|
||||
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 {
|
||||
LOG_CONTEXT_NAME,
|
||||
LOG_MENU,
|
||||
LOG_MESSAGE_ID,
|
||||
panel_names,
|
||||
} from "../../utils/Config";
|
||||
import { SpotlightIds } from "../../utils/SpotlightIds";
|
||||
import NoSearchResults from "./NoSearchResults/NoSearchResults";
|
||||
import RecommendedKeywords from "./RecommendedKeywords/RecommendedKeywords";
|
||||
import css from "./SearchPanel.new.module.less";
|
||||
import SearchResults from "./SearchResults/SearchResults";
|
||||
} from '../../utils/Config';
|
||||
import { SpotlightIds } from '../../utils/SpotlightIds';
|
||||
import NoSearchResults from './NoSearchResults/NoSearchResults';
|
||||
import RecommendedKeywords from './RecommendedKeywords/RecommendedKeywords';
|
||||
import css from './SearchPanel.new.module.less';
|
||||
import SearchResultsNew from './SearchResults.new';
|
||||
import SearchResults from './SearchResults/SearchResults';
|
||||
|
||||
const ContainerBasic = SpotlightContainerDecorator(
|
||||
{ enterTo: "last-focused" },
|
||||
@@ -82,10 +104,15 @@ const SPOTLIGHT_IDS = {
|
||||
TOP_SEARCHES_SECTION: "top-searches-section",
|
||||
POPULAR_BRANDS_SECTION: "popular-brands-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 loadingComplete = useSelector((state) => state.common?.loadingComplete);
|
||||
const recommandedKeywords = useSelector(
|
||||
@@ -105,6 +132,18 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
const [position, setPosition] = useState(null);
|
||||
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 isOnTopRef = usePrevious(isOnTop);
|
||||
|
||||
@@ -122,43 +161,62 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
);
|
||||
|
||||
// 가짜 데이터 - 실제로는 Redux store나 API에서 가져와야 함
|
||||
const recentSearches = useMemo(() => ["Puppy food", "Dog toy", "Fitness"], []);
|
||||
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,
|
||||
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
|
||||
}
|
||||
], []);
|
||||
const recentSearches = useMemo(
|
||||
() => ["Puppy food", "Dog toy", "Fitness"],
|
||||
[]
|
||||
);
|
||||
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,
|
||||
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,
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingComplete && !recommandedKeywords) {
|
||||
@@ -250,7 +308,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
domain: "theme,show,item",
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
// 검색 시작 알림 (선택사항)
|
||||
dispatch(showSuccessToast(`"${query}" 검색 중...`, { duration: 2000 }));
|
||||
} else {
|
||||
@@ -305,47 +363,65 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleKeydown = useCallback((e) => {
|
||||
if (!isOnTopRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter 키 처리
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
if (showVirtualKeyboard) {
|
||||
// 가상 키보드가 열려있으면 검색 실행하고 키보드 닫기
|
||||
handleSearchSubmit(searchQuery);
|
||||
} else {
|
||||
// 가상 키보드가 닫혀있으면 키보드 열기
|
||||
setShowVirtualKeyboard(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 방향키 처리 - Spotlight 네비게이션 허용
|
||||
const arrowKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Left", "Right", "Up", "Down"];
|
||||
if (arrowKeys.includes(e.key)) {
|
||||
// 입력 필드가 비어있고 왼쪽 화살표인 경우에만 방지
|
||||
if (position === 0 && (e.key === "Left" || e.key === "ArrowLeft") && !searchQuery) {
|
||||
e.preventDefault();
|
||||
const handleKeydown = useCallback(
|
||||
(e) => {
|
||||
if (!isOnTopRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 오른쪽 화살표 키 처리 - 포커스 이동 허용
|
||||
if (e.key === "ArrowRight" || e.key === "Right") {
|
||||
// 커서가 텍스트 끝에 있을 때만 포커스 이동 허용
|
||||
const input = document.querySelector(`[data-spotlight-id="input-field-box"] > input`);
|
||||
if (input && position === input.value.length) {
|
||||
// 커서가 텍스트 끝에 있으면 포커스 이동 허용
|
||||
|
||||
// Enter 키 처리
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
if (showVirtualKeyboard) {
|
||||
// 가상 키보드가 열려있으면 검색 실행하고 키보드 닫기
|
||||
handleSearchSubmit(searchQuery);
|
||||
} else {
|
||||
// 가상 키보드가 닫혀있으면 키보드 열기
|
||||
setShowVirtualKeyboard(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 방향키 처리 - Spotlight 네비게이션 허용
|
||||
const arrowKeys = [
|
||||
"ArrowLeft",
|
||||
"ArrowRight",
|
||||
"ArrowUp",
|
||||
"ArrowDown",
|
||||
"Left",
|
||||
"Right",
|
||||
"Up",
|
||||
"Down",
|
||||
];
|
||||
if (arrowKeys.includes(e.key)) {
|
||||
// 입력 필드가 비어있고 왼쪽 화살표인 경우에만 방지
|
||||
if (
|
||||
position === 0 &&
|
||||
(e.key === "Left" || e.key === "ArrowLeft") &&
|
||||
!searchQuery
|
||||
) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// 오른쪽 화살표 키 처리 - 포커스 이동 허용
|
||||
if (e.key === "ArrowRight" || e.key === "Right") {
|
||||
// 커서가 텍스트 끝에 있을 때만 포커스 이동 허용
|
||||
const input = document.querySelector(
|
||||
`[data-spotlight-id="input-field-box"] > input`
|
||||
);
|
||||
if (input && position === input.value.length) {
|
||||
// 커서가 텍스트 끝에 있으면 포커스 이동 허용
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 나머지 방향키는 Spotlight가 처리하도록 허용
|
||||
return;
|
||||
}
|
||||
|
||||
// 나머지 방향키는 Spotlight가 처리하도록 허용
|
||||
return;
|
||||
}
|
||||
}, [searchQuery, position, handleSearchSubmit, showVirtualKeyboard]);
|
||||
},
|
||||
[searchQuery, position, handleSearchSubmit, showVirtualKeyboard]
|
||||
);
|
||||
|
||||
const cursorPosition = useCallback(() => {
|
||||
const input = document.querySelector(
|
||||
@@ -397,12 +473,19 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}, [panelInfo, firstSpot]);
|
||||
|
||||
// 키워드 클릭 핸들러
|
||||
const handleKeywordClick = useCallback((keyword) => {
|
||||
setSearchQuery(keyword);
|
||||
handleSearchSubmit(keyword);
|
||||
// 키워드 선택 알림
|
||||
dispatch(showSuccessToast(`"${keyword}" 키워드로 검색합니다.`, { duration: 2000 }));
|
||||
}, [handleSearchSubmit, dispatch]);
|
||||
const handleKeywordClick = useCallback(
|
||||
(keyword) => {
|
||||
setSearchQuery(keyword);
|
||||
handleSearchSubmit(keyword);
|
||||
// 키워드 선택 알림
|
||||
dispatch(
|
||||
showSuccessToast(`"${keyword}" 키워드로 검색합니다.`, {
|
||||
duration: 2000,
|
||||
})
|
||||
);
|
||||
},
|
||||
[handleSearchSubmit, dispatch]
|
||||
);
|
||||
|
||||
// 상품 클릭 핸들러
|
||||
const handleProductClick = useCallback((product) => {
|
||||
@@ -417,58 +500,62 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}, [dispatch]);
|
||||
|
||||
// ProductCard 컴포넌트
|
||||
const ProductCard = useCallback(({
|
||||
product,
|
||||
index,
|
||||
onClick,
|
||||
showForYouBadge = true,
|
||||
showBrandLogo = true,
|
||||
showBrandName = true,
|
||||
showProductTitle = true,
|
||||
badgeText = "For You",
|
||||
badgePosition = "top-right"
|
||||
}) => {
|
||||
return (
|
||||
<SpottableProduct
|
||||
key={`product-${index}`}
|
||||
className={css.productCard}
|
||||
onClick={() => onClick?.(product)}
|
||||
spotlightId={`product-${index}`}
|
||||
>
|
||||
<div className={css.productImageWrapper}>
|
||||
<img
|
||||
src={product.image}
|
||||
alt={product.title}
|
||||
className={css.productImage}
|
||||
/>
|
||||
{showForYouBadge && product.isForYou && (
|
||||
<div className={`${css.forYouBadge} ${css[badgePosition]}`}>
|
||||
{badgeText}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={css.productInfo}>
|
||||
{showBrandLogo && (
|
||||
<div className={css.productBrandWrapper}>
|
||||
<img
|
||||
src={product.brandLogo}
|
||||
alt={product.brandName}
|
||||
className={css.brandLogo}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={css.productDetails}>
|
||||
{showBrandName && (
|
||||
<div className={css.brandName}>{product.brandName}</div>
|
||||
)}
|
||||
{showProductTitle && (
|
||||
<div className={css.productTitle}>{product.title}</div>
|
||||
)}
|
||||
const renderItem = useCallback(
|
||||
(
|
||||
// {
|
||||
// product,
|
||||
// index,
|
||||
// onClick,
|
||||
// showBrandLogo = true,
|
||||
// showBrandName = true,
|
||||
// showProductTitle = true,
|
||||
// ...rest
|
||||
// }
|
||||
{ index, ...rest }
|
||||
) => {
|
||||
const {
|
||||
showBrandLogo = true,
|
||||
showBrandName = true,
|
||||
showProductTitle = true,
|
||||
image,
|
||||
title,
|
||||
brandLogo,
|
||||
brandName,
|
||||
} = hotPicks[index];
|
||||
return (
|
||||
<SpottableProduct
|
||||
key={`product-${index}`}
|
||||
className={css.productCard}
|
||||
spotlightId={`product-${index}`}
|
||||
{...rest}
|
||||
>
|
||||
<div className={css.productImageWrapper}>
|
||||
<img src={image} alt={title} className={css.productImage} />
|
||||
</div>
|
||||
</div>
|
||||
</SpottableProduct>
|
||||
);
|
||||
}, []);
|
||||
<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>
|
||||
</SpottableProduct>
|
||||
);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<TPanel
|
||||
@@ -494,8 +581,12 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
scrollable={true}
|
||||
>
|
||||
{/* 검색 입력 영역 */}
|
||||
<InputContainer
|
||||
className={css.inputContainer}
|
||||
<InputContainer
|
||||
className={classNames(
|
||||
css.inputContainer,
|
||||
inputFocus === true && css.inputFocus,
|
||||
css.searchValue /* 이건 결과값 있을때만. 조건 추가필요 */
|
||||
)}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.SEARCH_INPUT_LAYER}
|
||||
>
|
||||
@@ -520,35 +611,37 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
tabIndex={0}
|
||||
spotlightDisabled={false}
|
||||
spotlightBoxDisabled={true}
|
||||
onFocus={_onFocus}
|
||||
onBlur={_onBlur}
|
||||
/>
|
||||
<SpottableMicButton
|
||||
className={css.microphoneButton}
|
||||
<SpottableMicButton
|
||||
className={css.microphoneButton}
|
||||
onClick={onCancel}
|
||||
spotlightId={SPOTLIGHT_IDS.MICROPHONE_BUTTON}
|
||||
>
|
||||
<div className={css.microphoneCircle}>
|
||||
<img
|
||||
src={micIcon}
|
||||
alt="Microphone"
|
||||
<img
|
||||
src={micIcon}
|
||||
alt="Microphone"
|
||||
className={css.microphoneIcon}
|
||||
/>
|
||||
</div>
|
||||
</SpottableMicButton>
|
||||
|
||||
|
||||
{/* 테스트용 Toast 버튼 (개발용) */}
|
||||
<SpottableMicButton
|
||||
className={css.testToastButton}
|
||||
{/* <SpottableMicButton
|
||||
className={css.testToastButton}
|
||||
onClick={handleTestToasts}
|
||||
spotlightId="test-toast-button"
|
||||
>
|
||||
<div className={css.testButtonCircle}>
|
||||
🧪
|
||||
</div>
|
||||
</SpottableMicButton>
|
||||
<div className={css.testButtonCircle}>🧪</div>
|
||||
</SpottableMicButton> */}
|
||||
</div>
|
||||
</InputContainer>
|
||||
|
||||
{/* 검색 결과 표시 영역 */}
|
||||
{/* 결과갑 부분 작업중 */}
|
||||
<SearchResultsNew />
|
||||
{/* //결과갑 부분 작업중 */}
|
||||
{/* 검색 결과 표시 영역
|
||||
{searchPerformed && searchQuery !== null ? (
|
||||
Object.keys(searchDatas).length > 0 ? (
|
||||
<SearchResults
|
||||
@@ -561,117 +654,139 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
)
|
||||
) : (
|
||||
<ContainerBasic className={css.contentContainer}>
|
||||
{/* 최근 검색어 섹션 */}
|
||||
<SectionContainer
|
||||
className={css.section}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.RECENT_SEARCHES_SECTION}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>Your Recent Searches</div>
|
||||
</div>
|
||||
<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>
|
||||
</SectionContainer>
|
||||
{/* 노출 조건 변경 필요. 포커스 블러만으로는 안됌.(가상 키보드 노출시가 맞을듯)
|
||||
{inputFocus === false ? (
|
||||
<>
|
||||
{/* 최근 검색어 섹션
|
||||
<SectionContainer
|
||||
className={css.section}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.RECENT_SEARCHES_SECTION}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>
|
||||
Your Recent Searches
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</SectionContainer>
|
||||
|
||||
{/* 인기 검색어 섹션 */}
|
||||
<SectionContainer
|
||||
className={css.section}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.TOP_SEARCHES_SECTION}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>Top Searches</div>
|
||||
</div>
|
||||
<div className={css.keywordList}>
|
||||
{topSearches.map((keyword, index) => (
|
||||
<SpottableKeyword
|
||||
key={`top-${index}`}
|
||||
className={css.keywordButton}
|
||||
onClick={() => handleKeywordClick(keyword)}
|
||||
spotlightId={`top-keyword-${index}`}
|
||||
>
|
||||
{keyword}
|
||||
</SpottableKeyword>
|
||||
))}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
{/* 인기 검색어 섹션
|
||||
<SectionContainer
|
||||
className={css.section}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.TOP_SEARCHES_SECTION}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>Top Searches</div>
|
||||
</div>
|
||||
<div className={css.keywordList}>
|
||||
{topSearches.map((keyword, index) => (
|
||||
<SpottableKeyword
|
||||
key={`top-${index}`}
|
||||
className={css.keywordButton}
|
||||
onClick={() => handleKeywordClick(keyword)}
|
||||
spotlightId={`top-keyword-${index}`}
|
||||
>
|
||||
{keyword}
|
||||
</SpottableKeyword>
|
||||
))}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
|
||||
{/* 인기 브랜드 섹션 */}
|
||||
<SectionContainer
|
||||
className={css.section}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.POPULAR_BRANDS_SECTION}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>Popular Brands</div>
|
||||
</div>
|
||||
<div className={css.keywordList}>
|
||||
{popularBrands.map((brand, index) => (
|
||||
<SpottableKeyword
|
||||
key={`brand-${index}`}
|
||||
className={css.keywordButton}
|
||||
onClick={() => handleKeywordClick(brand)}
|
||||
spotlightId={`brand-${index}`}
|
||||
>
|
||||
{brand}
|
||||
</SpottableKeyword>
|
||||
))}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
{/* 인기 브랜드 섹션
|
||||
<SectionContainer
|
||||
className={css.section}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.POPULAR_BRANDS_SECTION}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>Popular Brands</div>
|
||||
</div>
|
||||
<div className={css.keywordList}>
|
||||
{popularBrands.map((brand, index) => (
|
||||
<SpottableKeyword
|
||||
key={`brand-${index}`}
|
||||
className={css.keywordButton}
|
||||
onClick={() => handleKeywordClick(brand)}
|
||||
spotlightId={`brand-${index}`}
|
||||
>
|
||||
{brand}
|
||||
</SpottableKeyword>
|
||||
))}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
|
||||
{/* Hot Picks for You 섹션 */}
|
||||
<SectionContainer
|
||||
className={css.section}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.HOT_PICKS_SECTION}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>Hot Picks for You</div>
|
||||
{/* Hot Picks for You 섹션
|
||||
<SectionContainer
|
||||
className={css.hotpicksSection}
|
||||
data-wheel-point={true}
|
||||
spotlightId={SPOTLIGHT_IDS.HOT_PICKS_SECTION}
|
||||
>
|
||||
<div className={css.sectionHeader}>
|
||||
<div className={css.sectionIndicator}></div>
|
||||
<div className={css.sectionTitle}>
|
||||
Hot Picks for You
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.productList}>
|
||||
{hotPicks && hotPicks.length > 0 && (
|
||||
<TVirtualGridList
|
||||
dataSize={hotPicks.length}
|
||||
direction="horizontal"
|
||||
renderItem={renderItem}
|
||||
// itemWidth={546}
|
||||
itemWidth={416}
|
||||
itemHeight={436}
|
||||
spacing={20}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</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>
|
||||
<div className={css.productList}>
|
||||
{hotPicks.map((product, index) => (
|
||||
<ProductCard
|
||||
key={`product-${index}`}
|
||||
product={product}
|
||||
index={index}
|
||||
onClick={handleProductClick}
|
||||
showForYouBadge={true}
|
||||
showBrandLogo={true}
|
||||
showBrandName={true}
|
||||
showProductTitle={true}
|
||||
badgeText="For You"
|
||||
badgePosition="top-right"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</SectionContainer>
|
||||
)}
|
||||
</ContainerBasic>
|
||||
)}
|
||||
*/}
|
||||
</TVerticalPagenator>
|
||||
)}
|
||||
</ContainerBasic>
|
||||
</TBody>
|
||||
|
||||
|
||||
{/* Virtual Keyboard */}
|
||||
<VirtualKeyboardContainer
|
||||
{/* <VirtualKeyboardContainer
|
||||
isVisible={showVirtualKeyboard}
|
||||
onClose={() => setShowVirtualKeyboard(false)}
|
||||
/>
|
||||
/> */}
|
||||
</TPanel>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,16 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.inputFocus {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
&.searchValue {
|
||||
padding-bottom: 55px;
|
||||
padding-top: 55px;
|
||||
}
|
||||
> * {
|
||||
margin-bottom: 10px;
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -45,7 +51,7 @@
|
||||
|
||||
> * {
|
||||
margin-right: 15px;
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -71,12 +77,12 @@
|
||||
padding: 20px 40px 20px 50px !important;
|
||||
border: none !important;
|
||||
background-color: #fff !important;
|
||||
|
||||
|
||||
input {
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
color: black;
|
||||
font-size: 42px;
|
||||
font-family: 'LG Smart UI';
|
||||
font-family: "LG Smart UI";
|
||||
font-weight: 700;
|
||||
line-height: 42px;
|
||||
outline: none;
|
||||
@@ -89,13 +95,13 @@
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
|
||||
// TInput 내부 컨테이너의 포커스 스타일 완전 제거
|
||||
&[data-spotlight-container="true"] {
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
outline: none !important;
|
||||
@@ -110,7 +116,7 @@
|
||||
border: 5px solid @PRIMARY_COLOR_RED;
|
||||
box-shadow: 0 0 22px 0 rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
|
||||
// TInput 컴포넌트 자체의 내부 포커스 스타일 제거
|
||||
> div {
|
||||
&:focus,
|
||||
@@ -120,7 +126,7 @@
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 모든 내부 요소의 포커스 스타일 완전 제거
|
||||
* {
|
||||
&:focus,
|
||||
@@ -130,13 +136,13 @@
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TInput 내부 Container의 포커스 제거
|
||||
> div[data-spotlight-container="true"] {
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
outline: none !important;
|
||||
@@ -144,13 +150,13 @@
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// InputField의 포커스 제거
|
||||
input {
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
outline: none !important;
|
||||
@@ -166,13 +172,13 @@
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
content: "";
|
||||
width: 36.27px;
|
||||
height: 36.27px;
|
||||
position: absolute;
|
||||
left: 1.95px;
|
||||
top: 1.95px;
|
||||
border: 3.90px solid black;
|
||||
border: 3.9px solid black;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
@@ -202,10 +208,7 @@
|
||||
align-items: center;
|
||||
|
||||
.microphoneIcon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
padding: 10px;
|
||||
height: 50px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
@@ -223,7 +226,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 테스트용 Toast 버튼 스타일
|
||||
.testToastButton {
|
||||
width: 100px;
|
||||
@@ -277,6 +280,64 @@
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 섹션 공통 스타일
|
||||
@@ -291,7 +352,7 @@
|
||||
|
||||
> * {
|
||||
margin-bottom: 10px;
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -306,7 +367,7 @@
|
||||
|
||||
> * {
|
||||
margin-right: 12px;
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -315,14 +376,14 @@
|
||||
.sectionIndicator {
|
||||
width: 6px;
|
||||
height: 36px;
|
||||
background: #C70850;
|
||||
background: #c70850;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
text-align: center;
|
||||
color: black;
|
||||
font-size: 42px;
|
||||
font-family: 'LG Smart UI';
|
||||
font-family: "LG Smart UI";
|
||||
font-weight: 700;
|
||||
line-height: 42px;
|
||||
word-wrap: break-word;
|
||||
@@ -341,17 +402,13 @@
|
||||
> * {
|
||||
margin-right: 19px;
|
||||
margin-bottom: 19px;
|
||||
|
||||
&:nth-child(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.keywordButton {
|
||||
padding: 20px;
|
||||
background: white;
|
||||
border-radius: 100px;
|
||||
border: 5px solid #DADADA;
|
||||
border: 5px solid #dadada;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
@@ -361,7 +418,7 @@
|
||||
|
||||
> * {
|
||||
margin-bottom: 10px;
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -369,7 +426,7 @@
|
||||
|
||||
color: black;
|
||||
font-size: 24px;
|
||||
font-family: 'LG Smart UI';
|
||||
font-family: "LG Smart UI";
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
@@ -388,50 +445,68 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 상품 리스트 스타일 (Hot Picks for You)
|
||||
.productList {
|
||||
align-self: stretch;
|
||||
padding-top: 30px;
|
||||
.hotpicksSection {
|
||||
padding-top: 63px;
|
||||
padding-left: 60px;
|
||||
width: 1800px;
|
||||
height: 580px;
|
||||
.sectionHeader {
|
||||
width: 1800px;
|
||||
height: 42px;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
margin-right: 19px;
|
||||
margin-bottom: 19px;
|
||||
|
||||
&:nth-child(2n) {
|
||||
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;
|
||||
border: 5px solid #dadada;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
display: inline-flex;
|
||||
|
||||
> * {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: @PRIMARY_COLOR_RED;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@@ -454,51 +529,6 @@
|
||||
object-fit: cover;
|
||||
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 {
|
||||
@@ -511,7 +541,7 @@
|
||||
|
||||
> * {
|
||||
margin-right: 10px;
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -524,7 +554,7 @@
|
||||
|
||||
> * {
|
||||
margin-right: 10px;
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
@@ -547,7 +577,7 @@
|
||||
|
||||
> * {
|
||||
margin-bottom: 10px;
|
||||
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -557,7 +587,7 @@
|
||||
text-align: center;
|
||||
color: #808080;
|
||||
font-size: 18px;
|
||||
font-family: 'LG Smart UI';
|
||||
font-family: "LG Smart UI";
|
||||
font-weight: 700;
|
||||
line-height: 18px;
|
||||
word-wrap: break-word;
|
||||
@@ -567,7 +597,7 @@
|
||||
align-self: stretch;
|
||||
color: black;
|
||||
font-size: 24px;
|
||||
font-family: 'LG Smart UI';
|
||||
font-family: "LG Smart UI";
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
word-wrap: break-word;
|
||||
@@ -584,11 +614,11 @@
|
||||
-ms-overflow-style: none;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
// 스크롤 동작 개선
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
@@ -606,7 +636,6 @@
|
||||
[data-spotlight-id] {
|
||||
&:focus {
|
||||
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