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 from "react";
|
||||
import React, { forwardRef, useCallback, useRef, useEffect } from "react";
|
||||
import css from "./TInput.module.less";
|
||||
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 ICONS = { search: "search" };
|
||||
@@ -10,27 +15,33 @@ const BORDER = { none: "none" };
|
||||
const COLOR = { transparent: "transparent" };
|
||||
|
||||
export default function TInput({
|
||||
kind = "",
|
||||
icon = null,
|
||||
border = null,
|
||||
color = null,
|
||||
className = null,
|
||||
kind,
|
||||
icon,
|
||||
border,
|
||||
color,
|
||||
className,
|
||||
spotlightDisabled,
|
||||
...rest
|
||||
}) {
|
||||
const handleIconClick = useCallback(() => {}, []);
|
||||
|
||||
return (
|
||||
<Container
|
||||
className={classNames(css.container, className ? className : null)}
|
||||
>
|
||||
<InputField
|
||||
{...rest}
|
||||
className={classNames(
|
||||
css.input,
|
||||
icon && css[icon],
|
||||
css[kind],
|
||||
css[border],
|
||||
css[color],
|
||||
className
|
||||
)}
|
||||
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/utils.module.less";
|
||||
|
||||
.container {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
position: relative;
|
||||
|
||||
.input {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
@@ -9,6 +14,7 @@
|
||||
border-radius: 50px;
|
||||
border: 5px solid #ccc;
|
||||
background-color: #fff !important;
|
||||
width: 100% !important;
|
||||
|
||||
&:focus-within,
|
||||
&:hover {
|
||||
@@ -27,3 +33,30 @@
|
||||
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_PANEL: "debugpanel",
|
||||
};
|
||||
|
||||
//button
|
||||
export const TBUTTON_PRESS_DELAY = 100;
|
||||
|
||||
@@ -12,7 +12,9 @@ import css from "./OnSalePanel.module.less";
|
||||
export default function OnSalePanel() {
|
||||
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 [currentLgCatCd, setCurrentLgCatCd] = useState();
|
||||
@@ -34,7 +36,9 @@ export default function OnSalePanel() {
|
||||
|
||||
useEffect(() => {
|
||||
if (currentLgCatCd) {
|
||||
dispatch(getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: currentLgCatCd }));
|
||||
dispatch(
|
||||
getOnSaleInfo({ categoryIncFlag: "Y", lgCatCd: currentLgCatCd })
|
||||
);
|
||||
}
|
||||
}, [currentLgCatCd]);
|
||||
|
||||
@@ -56,7 +60,12 @@ export default function OnSalePanel() {
|
||||
return (
|
||||
<OnSaleProductsGrid key={saleNm} saleNm={saleNm}>
|
||||
{saleProductInfos.map((saleProduct) => {
|
||||
return <OnSaleProductCard key={saleProduct.prdtId} saleProduct={saleProduct} />;
|
||||
return (
|
||||
<OnSaleProductCard
|
||||
key={saleProduct.prdtId}
|
||||
saleProduct={saleProduct}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</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 TInput from "../../components/TInput/TInput";
|
||||
import TInput, { KINDS, ICONS } from "../../components/TInput/TInput";
|
||||
import TPanel from "../../components/TPanel/TPanel";
|
||||
import TButton from "../../components/TButton/TButton";
|
||||
import { $L } from "../../utils/helperMethods";
|
||||
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;
|
||||
|
||||
export default function SearchPanel() {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const recommandedKeywords = useSelector(
|
||||
(state) => state.myPage.recommandedKeywordData.data?.keywords
|
||||
);
|
||||
@@ -35,25 +45,39 @@ export default function SearchPanel() {
|
||||
setCurrentPage((prev) => (prev > 1 ? prev - 1 : prev));
|
||||
}, [currentPage]);
|
||||
|
||||
const hasPrevPage = currentPage > 1;
|
||||
const hasNextPage =
|
||||
currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length;
|
||||
|
||||
return (
|
||||
<TPanel className={css.panel}>
|
||||
<TInput className={css.input} autoFocus />
|
||||
<div>
|
||||
<TInput
|
||||
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) => (
|
||||
<div key={index}>{keyword.keywd}</div>
|
||||
<TButton key={index} className={css.keywordBox}>
|
||||
{keyword.keywd}
|
||||
</TButton>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<TButton onClick={handlePrev} disabled={currentPage === 1}>
|
||||
PREV
|
||||
</TButton>
|
||||
<TButton
|
||||
</Container>
|
||||
{hasNextPage && (
|
||||
<TIconButton
|
||||
iconType={ICON_TYPES.rightArrow}
|
||||
className={classNames(css.arrow, css.arrowRight)}
|
||||
onClick={handleNext}
|
||||
disabled={currentPage * ITEMS_PER_PAGE >= recommandedKeywords?.length}
|
||||
>
|
||||
NEXT
|
||||
</TButton>
|
||||
</div>
|
||||
/>
|
||||
)}
|
||||
</TPanel>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,60 @@
|
||||
background-color: @BG_COLOR_01;
|
||||
width: 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