TIconButton, TInpu 추가 및 SearchPanel 작언 중
This commit is contained in:
BIN
com.twin.app.shoptime/assets/searchpanel/ico_search_submit.png
Normal file
BIN
com.twin.app.shoptime/assets/searchpanel/ico_search_submit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
com.twin.app.shoptime/assets/searchpanel/sch-arr-l-f.png
Normal file
BIN
com.twin.app.shoptime/assets/searchpanel/sch-arr-l-f.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
com.twin.app.shoptime/assets/searchpanel/sch-arr-l.png
Normal file
BIN
com.twin.app.shoptime/assets/searchpanel/sch-arr-l.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
com.twin.app.shoptime/assets/searchpanel/sch-arr-r-f.png
Normal file
BIN
com.twin.app.shoptime/assets/searchpanel/sch-arr-r-f.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
com.twin.app.shoptime/assets/searchpanel/sch-arr-r.png
Normal file
BIN
com.twin.app.shoptime/assets/searchpanel/sch-arr-r.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
106
com.twin.app.shoptime/src/components/TIconButton/TIconButton.jsx
Normal file
106
com.twin.app.shoptime/src/components/TIconButton/TIconButton.jsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import React, { useCallback, useRef, useState } from "react";
|
||||||
|
import css from "./TIconButton.module.less";
|
||||||
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { Job } from "@enact/core/util";
|
||||||
|
import * as Config from "../../utils/Config";
|
||||||
|
import { popPanel, resetPanels } from "../../features/panels/panelsSlice";
|
||||||
|
|
||||||
|
const SpottableComponent = Spottable("div");
|
||||||
|
|
||||||
|
const SIZES = { tiny: "tiny", small: "small" };
|
||||||
|
const ICON_TYPES = {
|
||||||
|
none: "none",
|
||||||
|
back: "back",
|
||||||
|
home: "home",
|
||||||
|
leftArrow: "leftArrow",
|
||||||
|
rightArrow: "rightArrow",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function TIconButton({
|
||||||
|
className,
|
||||||
|
iconType = ICON_TYPES.none,
|
||||||
|
size = "normal",
|
||||||
|
color,
|
||||||
|
spotlightId = undefined,
|
||||||
|
children,
|
||||||
|
onClick,
|
||||||
|
spotlightDisabled,
|
||||||
|
disabled,
|
||||||
|
...rest
|
||||||
|
}) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const [pressed, setPressed] = useState(false);
|
||||||
|
|
||||||
|
const clearPressedJob = useRef(
|
||||||
|
new Job((func) => {
|
||||||
|
setPressed(false);
|
||||||
|
|
||||||
|
setTimeout(func, Config.TBUTTON_PRESS_DELAY);
|
||||||
|
}, Config.TBUTTON_PRESS_DELAY)
|
||||||
|
);
|
||||||
|
|
||||||
|
const _onClick = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (disabled) {
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPressed(true);
|
||||||
|
|
||||||
|
clearPressedJob.current.start(() => {
|
||||||
|
if (onClick) {
|
||||||
|
onClick(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (iconType) {
|
||||||
|
case ICON_TYPES.back: {
|
||||||
|
dispatch(popPanel());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ICON_TYPES.home: {
|
||||||
|
dispatch(resetPanels());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[dispatch, iconType, onClick, disabled]
|
||||||
|
);
|
||||||
|
|
||||||
|
const _onBlur = useCallback(() => {
|
||||||
|
setPressed(false);
|
||||||
|
|
||||||
|
clearPressedJob.current.stop();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const _onFocus = useCallback(() => {}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SpottableComponent
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
className={classNames(
|
||||||
|
css.tIconButton,
|
||||||
|
css[iconType],
|
||||||
|
css[size],
|
||||||
|
disabled && css.disabled,
|
||||||
|
className ? className : null,
|
||||||
|
pressed && css.pressed
|
||||||
|
)}
|
||||||
|
onClick={_onClick}
|
||||||
|
onBlur={_onBlur}
|
||||||
|
onFocus={_onFocus}
|
||||||
|
spotlightDisabled={spotlightDisabled}
|
||||||
|
disabled={disabled}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SpottableComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ICON_TYPES, SIZES };
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
@import "../../style/CommonStyle.module.less";
|
||||||
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
|
.tIconButton {
|
||||||
|
background-color: transparent;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftArrow {
|
||||||
|
background-image: url("../../../assets/searchpanel/sch-arr-l.png");
|
||||||
|
background-position: center top;
|
||||||
|
width: 47px;
|
||||||
|
height: 92px;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
background-position: center bottom;
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightArrow {
|
||||||
|
background-image: url("../../../assets/searchpanel/sch-arr-r.png");
|
||||||
|
background-position: center top;
|
||||||
|
width: 47px;
|
||||||
|
height: 92px;
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
background-position: center bottom;
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"main": "TIconButton.jsx"
|
||||||
|
}
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
import classNames from "classnames";
|
import React, { forwardRef, useCallback, useRef, useEffect } from "react";
|
||||||
import React from "react";
|
|
||||||
import css from "./TInput.module.less";
|
import css from "./TInput.module.less";
|
||||||
import { InputField } from "@enact/sandstone/Input";
|
import { InputField } from "@enact/sandstone/Input";
|
||||||
import { $L } from "../../utils/helperMethods";
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator("div");
|
||||||
|
|
||||||
|
const SpottableInputIcon = Spottable("div");
|
||||||
|
|
||||||
const KINDS = { withIcon: "withIcon" };
|
const KINDS = { withIcon: "withIcon" };
|
||||||
const ICONS = { search: "search" };
|
const ICONS = { search: "search" };
|
||||||
@@ -10,27 +15,33 @@ const BORDER = { none: "none" };
|
|||||||
const COLOR = { transparent: "transparent" };
|
const COLOR = { transparent: "transparent" };
|
||||||
|
|
||||||
export default function TInput({
|
export default function TInput({
|
||||||
kind = "",
|
kind,
|
||||||
icon = null,
|
icon,
|
||||||
border = null,
|
border,
|
||||||
color = null,
|
color,
|
||||||
className = null,
|
className,
|
||||||
spotlightDisabled,
|
spotlightDisabled,
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
|
const handleIconClick = useCallback(() => {}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Container
|
||||||
|
className={classNames(css.container, className ? className : null)}
|
||||||
|
>
|
||||||
<InputField
|
<InputField
|
||||||
{...rest}
|
{...rest}
|
||||||
className={classNames(
|
|
||||||
css.input,
|
|
||||||
icon && css[icon],
|
|
||||||
css[kind],
|
|
||||||
css[border],
|
|
||||||
css[color],
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
spotlightDisabled={spotlightDisabled}
|
spotlightDisabled={spotlightDisabled}
|
||||||
|
className={classNames(css.input)}
|
||||||
|
spotlightId="input"
|
||||||
/>
|
/>
|
||||||
|
{kind === "withIcon" && (
|
||||||
|
<SpottableInputIcon
|
||||||
|
className={classNames(icon && css[icon])}
|
||||||
|
onClick={handleIconClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
@import "../../style/CommonStyle.module.less";
|
@import "../../style/CommonStyle.module.less";
|
||||||
@import "../../style/utils.module.less";
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
@@ -9,6 +14,7 @@
|
|||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
border: 5px solid #ccc;
|
border: 5px solid #ccc;
|
||||||
background-color: #fff !important;
|
background-color: #fff !important;
|
||||||
|
width: 100% !important;
|
||||||
|
|
||||||
&:focus-within,
|
&:focus-within,
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -27,3 +33,30 @@
|
|||||||
line-height: inherit !important;
|
line-height: inherit !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.withIcon {
|
||||||
|
> div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
position: absolute;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center top;
|
||||||
|
width: 41px;
|
||||||
|
height: 41px;
|
||||||
|
top: 50%;
|
||||||
|
right: 30px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-image: url("../../../assets/searchpanel/ico_search_submit.png");
|
||||||
|
|
||||||
|
&:focus-within,
|
||||||
|
&:hover {
|
||||||
|
width: 43px;
|
||||||
|
height: 43px;
|
||||||
|
transform: translateY(-50%) scale(1.1);
|
||||||
|
background-position: center bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,3 +25,6 @@ export const panel_names = {
|
|||||||
// debug
|
// debug
|
||||||
DEBUG_PANEL: "debugpanel",
|
DEBUG_PANEL: "debugpanel",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//button
|
||||||
|
export const TBUTTON_PRESS_DELAY = 100;
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import css from "./OnSalePanel.module.less";
|
|||||||
export default function OnSalePanel() {
|
export default function OnSalePanel() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const categoryInfos = useSelector((state) => state.onSale.onSaleData.categoryInfos);
|
const categoryInfos = useSelector(
|
||||||
|
(state) => state.onSale.onSaleData.categoryInfos
|
||||||
|
);
|
||||||
const saleInfos = useSelector((state) => state.onSale.onSaleData.saleInfos);
|
const saleInfos = useSelector((state) => state.onSale.onSaleData.saleInfos);
|
||||||
|
|
||||||
const [currentLgCatCd, setCurrentLgCatCd] = useState();
|
const [currentLgCatCd, setCurrentLgCatCd] = useState();
|
||||||
@@ -34,7 +36,9 @@ export default function OnSalePanel() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentLgCatCd) {
|
if (currentLgCatCd) {
|
||||||
dispatch(getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: currentLgCatCd }));
|
dispatch(
|
||||||
|
getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: currentLgCatCd })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [currentLgCatCd]);
|
}, [currentLgCatCd]);
|
||||||
|
|
||||||
@@ -56,7 +60,12 @@ export default function OnSalePanel() {
|
|||||||
return (
|
return (
|
||||||
<OnSaleProductsGrid key={saleNm} saleNm={saleNm}>
|
<OnSaleProductsGrid key={saleNm} saleNm={saleNm}>
|
||||||
{saleProductInfos.map((saleProduct) => {
|
{saleProductInfos.map((saleProduct) => {
|
||||||
return <OnSaleProductCard key={saleProduct.prdtId} saleProduct={saleProduct} />;
|
return (
|
||||||
|
<OnSaleProductCard
|
||||||
|
key={saleProduct.prdtId}
|
||||||
|
saleProduct={saleProduct}
|
||||||
|
/>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</OnSaleProductsGrid>
|
</OnSaleProductsGrid>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,16 +1,26 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState, useRef } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import TInput from "../../components/TInput/TInput";
|
import TInput, { KINDS, ICONS } from "../../components/TInput/TInput";
|
||||||
import TPanel from "../../components/TPanel/TPanel";
|
import TPanel from "../../components/TPanel/TPanel";
|
||||||
import TButton from "../../components/TButton/TButton";
|
import TButton from "../../components/TButton/TButton";
|
||||||
import { $L } from "../../utils/helperMethods";
|
import { $L } from "../../utils/helperMethods";
|
||||||
import css from "./SearchPanel.module.less";
|
import css from "./SearchPanel.module.less";
|
||||||
|
import TIconButton, {
|
||||||
|
ICON_TYPES,
|
||||||
|
SIZES,
|
||||||
|
} from "../../components/TIconButton/TIconButton";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
const ITEMS_PER_PAGE = 9;
|
const ITEMS_PER_PAGE = 9;
|
||||||
|
|
||||||
export default function SearchPanel() {
|
export default function SearchPanel() {
|
||||||
const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const recommandedKeywords = useSelector(
|
const recommandedKeywords = useSelector(
|
||||||
(state) => state.myPage.recommandedKeywordData.data?.keywords
|
(state) => state.myPage.recommandedKeywordData.data?.keywords
|
||||||
);
|
);
|
||||||
@@ -35,25 +45,39 @@ export default function SearchPanel() {
|
|||||||
setCurrentPage((prev) => (prev > 1 ? prev - 1 : prev));
|
setCurrentPage((prev) => (prev > 1 ? prev - 1 : prev));
|
||||||
}, [currentPage]);
|
}, [currentPage]);
|
||||||
|
|
||||||
|
const hasPrevPage = currentPage > 1;
|
||||||
|
const hasNextPage =
|
||||||
|
currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TPanel className={css.panel}>
|
<TPanel className={css.panel}>
|
||||||
<TInput className={css.input} autoFocus />
|
<TInput
|
||||||
<div>
|
className={css.input}
|
||||||
|
autoFocus
|
||||||
|
kind={KINDS.withIcon}
|
||||||
|
icon={ICONS.search}
|
||||||
|
/>
|
||||||
|
{hasPrevPage && (
|
||||||
|
<TIconButton
|
||||||
|
iconType={ICON_TYPES.leftArrow}
|
||||||
|
className={classNames(css.arrow, css.arrowLeft)}
|
||||||
|
onClick={handlePrev}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Container className={css.keywordsGrid}>
|
||||||
{paginatedKeywords.map((keyword, index) => (
|
{paginatedKeywords.map((keyword, index) => (
|
||||||
<div key={index}>{keyword.keywd}</div>
|
<TButton key={index} className={css.keywordBox}>
|
||||||
|
{keyword.keywd}
|
||||||
|
</TButton>
|
||||||
))}
|
))}
|
||||||
</div>
|
</Container>
|
||||||
<div>
|
{hasNextPage && (
|
||||||
<TButton onClick={handlePrev} disabled={currentPage === 1}>
|
<TIconButton
|
||||||
PREV
|
iconType={ICON_TYPES.rightArrow}
|
||||||
</TButton>
|
className={classNames(css.arrow, css.arrowRight)}
|
||||||
<TButton
|
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
disabled={currentPage * ITEMS_PER_PAGE >= recommandedKeywords?.length}
|
/>
|
||||||
>
|
)}
|
||||||
NEXT
|
|
||||||
</TButton>
|
|
||||||
</div>
|
|
||||||
</TPanel>
|
</TPanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,60 @@
|
|||||||
background-color: @BG_COLOR_01;
|
background-color: @BG_COLOR_01;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.flex(@direction: row);
|
|
||||||
|
> section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 880px;
|
||||||
|
margin: 0 auto 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
position: absolute;
|
||||||
|
top: 59%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrowLeft {
|
||||||
|
left: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrowRight {
|
||||||
|
right: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keywordsGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-gap: 80px;
|
||||||
|
width: 100%;
|
||||||
|
place-items: center;
|
||||||
|
padding: 0 233px;
|
||||||
|
|
||||||
|
.keywordBox {
|
||||||
|
width: 390px;
|
||||||
|
height: 97px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 8px 0 rgba(2, 3, 3, 0.2);
|
||||||
|
border: 1px solid #999;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #333;
|
||||||
|
font-family: @baseFontBold;
|
||||||
|
font-size: 40px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
overflow: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
box-shadow: 0 0 50px 0 rgba(11, 8, 8, 0.5);
|
||||||
|
border: 4px solid #c70850;
|
||||||
|
color: #c70850;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user