TIconButton, TInpu 추가 및 SearchPanel 작언 중

This commit is contained in:
hyunwoo93.cha
2024-01-26 18:31:37 +09:00
parent 15c2737623
commit fa4798dd3e
14 changed files with 338 additions and 60 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View 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 };

View File

@@ -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);
}
}

View File

@@ -0,0 +1,3 @@
{
"main": "TIconButton.jsx"
}

View File

@@ -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>
); );
} }

View File

@@ -1,7 +1,12 @@
@import "../../style/CommonStyle.module.less"; @import "../../style/CommonStyle.module.less";
@import "../../style/utils.module.less"; @import "../../style/utils.module.less";
.input { .container {
width: fit-content;
height: fit-content;
position: relative;
.input {
margin-right: 0; margin-right: 0;
margin-left: 0; margin-left: 0;
height: 100px !important; height: 100px !important;
@@ -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 {
@@ -26,4 +32,31 @@
height: inherit !important; height: inherit !important;
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;
}
}
} }

View File

@@ -25,3 +25,6 @@ export const panel_names = {
// debug // debug
DEBUG_PANEL: "debugpanel", DEBUG_PANEL: "debugpanel",
}; };
//button
export const TBUTTON_PRESS_DELAY = 100;

View File

@@ -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>
); );

View File

@@ -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>
); );
} }

View File

@@ -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;
}
}
}
} }