[251209] feat: NBCU-ShopByShow-1
🕐 커밋 시간: 2025. 12. 09. 14:33:20 📊 변경 통계: • 총 파일: 15개 • 추가: +128줄 • 삭제: -11줄 📁 추가된 파일: + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.jsx + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.module.less + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.jsx + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.module.less + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.jsx + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.module.less + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.jsx + com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.module.less 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/actionTypes.js ~ com.twin.app.shoptime/src/actions/brandActions.js ~ com.twin.app.shoptime/src/api/apiConfig.js ~ com.twin.app.shoptime/src/reducers/brandReducer.js ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBestSeller/FeaturedBestSellerList/FeaturedBestSellerList.jsx ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenuItem/QuickMenuItem.jsx 🔧 주요 변경 내용: • 타입 시스템 안정성 강화 • 핵심 비즈니스 로직 개선 • API 서비스 레이어 개선 • 중간 규모 기능 개선 • 모듈 구조 개선
This commit is contained in:
@@ -130,6 +130,7 @@ export const types = {
|
|||||||
GET_BRAND_CREATORS_INFO: 'GET_BRAND_CREATORS_INFO',
|
GET_BRAND_CREATORS_INFO: 'GET_BRAND_CREATORS_INFO',
|
||||||
GET_BRAND_SHOWROOM: 'GET_BRAND_SHOWROOM',
|
GET_BRAND_SHOWROOM: 'GET_BRAND_SHOWROOM',
|
||||||
GET_BRAND_RECENTLY_AIRED: 'GET_BRAND_RECENTLY_AIRED',
|
GET_BRAND_RECENTLY_AIRED: 'GET_BRAND_RECENTLY_AIRED',
|
||||||
|
GET_BRAND_SHOP_BY_SHOW: 'GET_BRAND_SHOP_BY_SHOW',
|
||||||
SET_BRAND_LIVE_CHANNEL_UPCOMING: 'SET_BRAND_LIVE_CHANNEL_UPCOMING',
|
SET_BRAND_LIVE_CHANNEL_UPCOMING: 'SET_BRAND_LIVE_CHANNEL_UPCOMING',
|
||||||
SET_BRAND_CHAN_INFO: 'SET_BRAND_CHAN_INFO',
|
SET_BRAND_CHAN_INFO: 'SET_BRAND_CHAN_INFO',
|
||||||
RESET_BRAND_STATE: 'RESET_BRAND_STATE',
|
RESET_BRAND_STATE: 'RESET_BRAND_STATE',
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ export const getBrandList = () => (dispatch, getState) => {
|
|||||||
export const getBrandLayoutInfo = (props) => (dispatch, getState) => {
|
export const getBrandLayoutInfo = (props) => (dispatch, getState) => {
|
||||||
const { patnrId } = props;
|
const { patnrId } = props;
|
||||||
|
|
||||||
|
console.log("[getBrandLayoutInfo] Called - patnrId:", patnrId);
|
||||||
|
|
||||||
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
|
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
// dlog("getBrandLayoutInfo onSuccess ", response.data);
|
console.log("[getBrandLayoutInfo] onSuccess - patnrId:", patnrId, "data:", response.data.data);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.GET_BRAND_LAYOUT_INFO,
|
type: types.GET_BRAND_LAYOUT_INFO,
|
||||||
@@ -53,6 +55,7 @@ export const getBrandLayoutInfo = (props) => (dispatch, getState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
|
console.log("[getBrandLayoutInfo] onFail - patnrId:", patnrId, "error:", error);
|
||||||
derror('getBrandLayoutInfo onFail ', error);
|
derror('getBrandLayoutInfo onFail ', error);
|
||||||
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
||||||
};
|
};
|
||||||
@@ -336,10 +339,12 @@ export const getBrandCategoryProductInfo = (props) => (dispatch, getState) => {
|
|||||||
export const getBrandBestSeller = (props) => (dispatch, getState) => {
|
export const getBrandBestSeller = (props) => (dispatch, getState) => {
|
||||||
const { patnrId } = props;
|
const { patnrId } = props;
|
||||||
|
|
||||||
|
console.log("[getBrandBestSeller] Called - patnrId:", patnrId);
|
||||||
|
|
||||||
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
|
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
// dlog("getBrandBestSeller onSuccess ", response.data);
|
console.log("[getBrandBestSeller] onSuccess - patnrId:", patnrId, "data:", response.data.data);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.GET_BRAND_BEST_SELLER,
|
type: types.GET_BRAND_BEST_SELLER,
|
||||||
@@ -352,6 +357,7 @@ export const getBrandBestSeller = (props) => (dispatch, getState) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onFail = (error) => {
|
const onFail = (error) => {
|
||||||
|
console.log("[getBrandBestSeller] onFail - patnrId:", patnrId, "error:", error);
|
||||||
derror('getBrandBestSeller onFail ', error);
|
derror('getBrandBestSeller onFail ', error);
|
||||||
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
||||||
};
|
};
|
||||||
@@ -386,6 +392,39 @@ export const getBrandShowroom = (props) => (dispatch, getState) => {
|
|||||||
TAxios(dispatch, getState, 'get', URLS.GET_BRAND_SHOWROOM, { patnrId }, {}, onSuccess, onFail);
|
TAxios(dispatch, getState, 'get', URLS.GET_BRAND_SHOWROOM, { patnrId }, {}, onSuccess, onFail);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Featured Brands SHOP BY SHOW 정보 조회 IF-LGSP-376
|
||||||
|
export const getBrandShopByShow = (props) => (dispatch, getState) => {
|
||||||
|
const { patnrId, contsId } = props;
|
||||||
|
|
||||||
|
console.log("[getBrandShopByShow] Called - patnrId:", patnrId, "contsId:", contsId);
|
||||||
|
|
||||||
|
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
|
||||||
|
|
||||||
|
const onSuccess = (response) => {
|
||||||
|
console.log("[getBrandShopByShow] onSuccess - patnrId:", patnrId, "data:", response.data.data);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: types.GET_BRAND_SHOP_BY_SHOW,
|
||||||
|
payload: {
|
||||||
|
data: response.data.data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFail = (error) => {
|
||||||
|
console.log("[getBrandShopByShow] onFail - patnrId:", patnrId, "error:", error);
|
||||||
|
derror('getBrandShopByShow onFail ', error);
|
||||||
|
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
||||||
|
};
|
||||||
|
|
||||||
|
// patnrId: 필수, contsId: 선택
|
||||||
|
const params = contsId ? { patnrId, contsId } : { patnrId };
|
||||||
|
|
||||||
|
TAxios(dispatch, getState, 'get', URLS.GET_BRAND_SHOP_BY_SHOW, params, {}, onSuccess, onFail);
|
||||||
|
};
|
||||||
|
|
||||||
// Featured Brands Recently Aired 조회 IF-LGSP-373
|
// Featured Brands Recently Aired 조회 IF-LGSP-373
|
||||||
export const getBrandRecentlyAired = (props) => (dispatch, getState) => {
|
export const getBrandRecentlyAired = (props) => (dispatch, getState) => {
|
||||||
const { patnrId } = props;
|
const { patnrId } = props;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export const URLS = {
|
|||||||
GET_BRAND_CREATORS_INFO: "/lgsp/v1/brand/creators.lge",
|
GET_BRAND_CREATORS_INFO: "/lgsp/v1/brand/creators.lge",
|
||||||
GET_BRAND_SHOWROOM: "/lgsp/v1/brand/showroom.lge",
|
GET_BRAND_SHOWROOM: "/lgsp/v1/brand/showroom.lge",
|
||||||
GET_BRAND_RECENTLY_AIRED: "/lgsp/v1/brand/recently/aired.lge",
|
GET_BRAND_RECENTLY_AIRED: "/lgsp/v1/brand/recently/aired.lge",
|
||||||
|
GET_BRAND_SHOP_BY_SHOW: "/lgsp/v1/brand/shopByShow.lge",
|
||||||
|
|
||||||
//on-sale controller
|
//on-sale controller
|
||||||
GET_ON_SALE_INFO: "/lgsp/v1/onsale/onsale.lge",
|
GET_ON_SALE_INFO: "/lgsp/v1/onsale/onsale.lge",
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ const initialState = {
|
|||||||
brandRecentlyAiredData: {
|
brandRecentlyAiredData: {
|
||||||
data: {},
|
data: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
brandShopByShowData: {
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const brandReducer = (state = initialState, action) => {
|
export const brandReducer = (state = initialState, action) => {
|
||||||
@@ -155,6 +159,12 @@ export const brandReducer = (state = initialState, action) => {
|
|||||||
brandRecentlyAiredData: action.payload,
|
brandRecentlyAiredData: action.payload,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case types.GET_BRAND_SHOP_BY_SHOW:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
brandShopByShowData: action.payload,
|
||||||
|
};
|
||||||
|
|
||||||
case types.SET_BRAND_LIVE_CHANNEL_UPCOMING:
|
case types.SET_BRAND_LIVE_CHANNEL_UPCOMING:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ export default function FeaturedBestSellerList({
|
|||||||
|
|
||||||
const panelInfo = useSelector((state) => state.panels.panels[0]?.panelInfo);
|
const panelInfo = useSelector((state) => state.panels.panels[0]?.panelInfo);
|
||||||
|
|
||||||
|
console.log("[FeaturedBestSellerList] Mounted - selectedPatnrId:", selectedPatnrId);
|
||||||
|
console.log("[FeaturedBestSellerList] brandBestSellerInfo:", brandBestSellerInfo);
|
||||||
|
console.log("[FeaturedBestSellerList] Data count:", brandBestSellerInfo?.length || 0);
|
||||||
|
|
||||||
const cursorVisible = useSelector(
|
const cursorVisible = useSelector(
|
||||||
(state) => state.common.appStatus.cursorVisible
|
(state) => state.common.appStatus.cursorVisible
|
||||||
);
|
);
|
||||||
@@ -155,6 +159,9 @@ export default function FeaturedBestSellerList({
|
|||||||
lgCatNm,
|
lgCatNm,
|
||||||
euEnrgLblInfos,
|
euEnrgLblInfos,
|
||||||
} = brandBestSellerInfo[index];
|
} = brandBestSellerInfo[index];
|
||||||
|
|
||||||
|
console.log("[FeaturedBestSellerList] renderItem - index:", index, "patnrId:", patnrId, "rankOrd:", rankOrd, "prdtNm:", prdtNm);
|
||||||
|
|
||||||
const rankText =
|
const rankText =
|
||||||
rankOrd === 1
|
rankOrd === 1
|
||||||
? rankOrd + "st,"
|
? rankOrd + "st,"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
getBrandLiveChannelInfo,
|
getBrandLiveChannelInfo,
|
||||||
getBrandRecommendedShowInfo,
|
getBrandRecommendedShowInfo,
|
||||||
getBrandSeriesInfo,
|
getBrandSeriesInfo,
|
||||||
|
getBrandShopByShow,
|
||||||
getBrandShowroom,
|
getBrandShowroom,
|
||||||
getBrandTSVInfo,
|
getBrandTSVInfo,
|
||||||
} from "../../actions/brandActions";
|
} from "../../actions/brandActions";
|
||||||
@@ -63,6 +64,7 @@ import LiveChannels from "./LiveChannels/LiveChannels";
|
|||||||
import QuickMenu from "./QuickMenu/QuickMenu";
|
import QuickMenu from "./QuickMenu/QuickMenu";
|
||||||
import RecommendedShows from "./RecommendedShows/RecommendedShows";
|
import RecommendedShows from "./RecommendedShows/RecommendedShows";
|
||||||
import Series from "./Series/Series";
|
import Series from "./Series/Series";
|
||||||
|
import ShopByShow from "./ShopByShow/ShopByShow";
|
||||||
import Showroom from "./Showroom/Showroom";
|
import Showroom from "./Showroom/Showroom";
|
||||||
import TodaysDeals from "./TodaysDeals/TodaysDeals";
|
import TodaysDeals from "./TodaysDeals/TodaysDeals";
|
||||||
import UpComing from "./UpComing/UpComing";
|
import UpComing from "./UpComing/UpComing";
|
||||||
@@ -90,6 +92,7 @@ const TEMPLATE_CODE_CONF = {
|
|||||||
SERIES: "BRD00107",
|
SERIES: "BRD00107",
|
||||||
CATEGORY: "BRD00108",
|
CATEGORY: "BRD00108",
|
||||||
SHOWROOM: "BRD00109",
|
SHOWROOM: "BRD00109",
|
||||||
|
NBCU: "BRD00110",
|
||||||
};
|
};
|
||||||
|
|
||||||
const DISPATCH_MAP = Object.freeze({
|
const DISPATCH_MAP = Object.freeze({
|
||||||
@@ -101,6 +104,7 @@ const DISPATCH_MAP = Object.freeze({
|
|||||||
[TEMPLATE_CODE_CONF.SERIES]: getBrandSeriesInfo,
|
[TEMPLATE_CODE_CONF.SERIES]: getBrandSeriesInfo,
|
||||||
[TEMPLATE_CODE_CONF.CATEGORY]: getBrandCategoryInfo,
|
[TEMPLATE_CODE_CONF.CATEGORY]: getBrandCategoryInfo,
|
||||||
[TEMPLATE_CODE_CONF.SHOWROOM]: getBrandShowroom,
|
[TEMPLATE_CODE_CONF.SHOWROOM]: getBrandShowroom,
|
||||||
|
[TEMPLATE_CODE_CONF.NBCU]: getBrandShopByShow,
|
||||||
});
|
});
|
||||||
|
|
||||||
const TOP_MARGIN = 36;
|
const TOP_MARGIN = 36;
|
||||||
@@ -263,6 +267,12 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
const brandShowroomInfo = useSelector(
|
const brandShowroomInfo = useSelector(
|
||||||
(state) => state.brand.brandShowroomData.data.brandShowroomInfo
|
(state) => state.brand.brandShowroomData.data.brandShowroomInfo
|
||||||
);
|
);
|
||||||
|
const brandShopByShowContsList = useSelector(
|
||||||
|
(state) => state.brand.brandShopByShowData.data.brandShopByShowContsList
|
||||||
|
);
|
||||||
|
const brandShopByShowContsInfo = useSelector(
|
||||||
|
(state) => state.brand.brandShopByShowData.data.brandShopByShowContsInfo
|
||||||
|
);
|
||||||
|
|
||||||
const [displayTopButton, setDisplayTopButton] = useState(false);
|
const [displayTopButton, setDisplayTopButton] = useState(false);
|
||||||
const [focusedContainerId, setFocusedContainerId] = useState(null);
|
const [focusedContainerId, setFocusedContainerId] = useState(null);
|
||||||
@@ -412,9 +422,12 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const renderPageItem = useCallback(() => {
|
const renderPageItem = useCallback(() => {
|
||||||
|
console.log("[FeaturedBrandsPanel] renderPageItem - sortedBrandLayoutInfo length:", sortedBrandLayoutInfo.length);
|
||||||
|
console.log("[FeaturedBrandsPanel] renderPageItem - sortedBrandLayoutInfo items:", sortedBrandLayoutInfo.map(el => el.shptmBrndOptTpCd));
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{sortedBrandLayoutInfo.map((el, idx) => {
|
{sortedBrandLayoutInfo.map((el, idx) => {
|
||||||
|
console.log("[FeaturedBrandsPanel] Processing template code:", el.shptmBrndOptTpCd);
|
||||||
switch (el.shptmBrndOptTpCd) {
|
switch (el.shptmBrndOptTpCd) {
|
||||||
case TEMPLATE_CODE_CONF.LIVE_CHANNELS: {
|
case TEMPLATE_CODE_CONF.LIVE_CHANNELS: {
|
||||||
return (
|
return (
|
||||||
@@ -485,6 +498,10 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case TEMPLATE_CODE_CONF.BEST_SELLER: {
|
case TEMPLATE_CODE_CONF.BEST_SELLER: {
|
||||||
|
console.log("[FeaturedBrandsPanel] BEST_SELLER - patnrId:", selectedPatnrId);
|
||||||
|
console.log("[FeaturedBrandsPanel] BEST_SELLER - hasTemplateCode:", hasTemplateCodeWithValue(sortedBrandLayoutInfo, TEMPLATE_CODE_CONF.BEST_SELLER));
|
||||||
|
console.log("[FeaturedBrandsPanel] BEST_SELLER - shouldRender:", shouldRenderComponent(brandBestSellerInfo));
|
||||||
|
console.log("[FeaturedBrandsPanel] BEST_SELLER - data:", brandBestSellerInfo);
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={el.shptmBrndOptTpCd}>
|
<React.Fragment key={el.shptmBrndOptTpCd}>
|
||||||
{hasTemplateCodeWithValue(
|
{hasTemplateCodeWithValue(
|
||||||
@@ -492,6 +509,8 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
TEMPLATE_CODE_CONF.BEST_SELLER
|
TEMPLATE_CODE_CONF.BEST_SELLER
|
||||||
) &&
|
) &&
|
||||||
shouldRenderComponent(brandBestSellerInfo) && (
|
shouldRenderComponent(brandBestSellerInfo) && (
|
||||||
|
<>
|
||||||
|
{console.log("[FeaturedBrandsPanel] Rendering FeaturedBestSeller for patnrId:", selectedPatnrId)}
|
||||||
<FeaturedBestSeller
|
<FeaturedBestSeller
|
||||||
brandBestSellerInfo={brandBestSellerInfo}
|
brandBestSellerInfo={brandBestSellerInfo}
|
||||||
handleItemFocus={handleItemFocus}
|
handleItemFocus={handleItemFocus}
|
||||||
@@ -501,6 +520,7 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
spotlightId={TEMPLATE_CODE_CONF.BEST_SELLER}
|
spotlightId={TEMPLATE_CODE_CONF.BEST_SELLER}
|
||||||
selectedPatnrId={selectedPatnrId}
|
selectedPatnrId={selectedPatnrId}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
@@ -650,6 +670,36 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case TEMPLATE_CODE_CONF.NBCU: {
|
||||||
|
console.log("[FeaturedBrandsPanel] NBCU - patnrId:", selectedPatnrId);
|
||||||
|
console.log("[FeaturedBrandsPanel] NBCU - hasTemplateCode:", hasTemplateCodeWithValue(sortedBrandLayoutInfo, TEMPLATE_CODE_CONF.NBCU));
|
||||||
|
console.log("[FeaturedBrandsPanel] NBCU - shouldRender:", shouldRenderComponent(brandShopByShowContsList));
|
||||||
|
console.log("[FeaturedBrandsPanel] NBCU - data:", brandShopByShowContsList);
|
||||||
|
return (
|
||||||
|
<React.Fragment key={el.shptmBrndOptTpCd}>
|
||||||
|
{hasTemplateCodeWithValue(
|
||||||
|
sortedBrandLayoutInfo,
|
||||||
|
TEMPLATE_CODE_CONF.NBCU
|
||||||
|
) &&
|
||||||
|
shouldRenderComponent(brandShopByShowContsList) && (
|
||||||
|
<>
|
||||||
|
{console.log("[FeaturedBrandsPanel] Rendering ShopByShow for patnrId:", selectedPatnrId)}
|
||||||
|
<ShopByShow
|
||||||
|
brandShopByShowContsList={brandShopByShowContsList}
|
||||||
|
brandShopByShowContsInfo={brandShopByShowContsInfo}
|
||||||
|
handleItemFocus={handleItemFocus}
|
||||||
|
order={idx + 1}
|
||||||
|
shelfOrder={el.expsOrd}
|
||||||
|
shelfTitle={el.shptmBrndOptTpNm}
|
||||||
|
spotlightId={TEMPLATE_CODE_CONF.NBCU}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
@@ -668,6 +718,8 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
brandSeriesGroupInfo,
|
brandSeriesGroupInfo,
|
||||||
brandSeriesInfo,
|
brandSeriesInfo,
|
||||||
brandShowroomInfo,
|
brandShowroomInfo,
|
||||||
|
brandShopByShowContsList,
|
||||||
|
brandShopByShowContsInfo,
|
||||||
brandTsvInfo,
|
brandTsvInfo,
|
||||||
fromGNB,
|
fromGNB,
|
||||||
fromQuickMenu,
|
fromQuickMenu,
|
||||||
@@ -711,6 +763,7 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
// effect: layout information fetching due to partner id change
|
// effect: layout information fetching due to partner id change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!fromDetail) {
|
if (!fromDetail) {
|
||||||
|
console.log("[FeaturedBrandsPanel] Layout Info Effect - patnrId:", panelInfo?.patnrId);
|
||||||
dispatch({ type: types.RESET_BRAND_LAYOUT_INFO });
|
dispatch({ type: types.RESET_BRAND_LAYOUT_INFO });
|
||||||
dispatch(getBrandLayoutInfo({ patnrId: panelInfo?.patnrId }));
|
dispatch(getBrandLayoutInfo({ patnrId: panelInfo?.patnrId }));
|
||||||
setIsInitialFocusOccurred(false);
|
setIsInitialFocusOccurred(false);
|
||||||
@@ -733,9 +786,12 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
|
|||||||
// effect: data fetching based on brandLayoutInfo and selectedPatnrId
|
// effect: data fetching based on brandLayoutInfo and selectedPatnrId
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sortedBrandLayoutInfo && selectedPatnrId) {
|
if (sortedBrandLayoutInfo && selectedPatnrId) {
|
||||||
|
console.log("[FeaturedBrandsPanel] Data Fetching Effect - selectedPatnrId:", selectedPatnrId);
|
||||||
|
console.log("[FeaturedBrandsPanel] sortedBrandLayoutInfo:", sortedBrandLayoutInfo);
|
||||||
Object.entries(DISPATCH_MAP) //
|
Object.entries(DISPATCH_MAP) //
|
||||||
.forEach(([templateCode, action]) => {
|
.forEach(([templateCode, action]) => {
|
||||||
if (hasTemplateCodeWithValue(sortedBrandLayoutInfo, templateCode)) {
|
if (hasTemplateCodeWithValue(sortedBrandLayoutInfo, templateCode)) {
|
||||||
|
console.log("[FeaturedBrandsPanel] Fetching data for template:", templateCode, "patnrId:", selectedPatnrId);
|
||||||
dispatch(action({ patnrId: selectedPatnrId }));
|
dispatch(action({ patnrId: selectedPatnrId }));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,10 +49,13 @@ const QuickMenuItem = ({
|
|||||||
}, [handleStopScrolling, itemIndex]);
|
}, [handleStopScrolling, itemIndex]);
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
|
console.log("[QuickMenuItem] Click - patnrId:", patnrId, "currentPatnrId:", selectedPatnrId ?? panelInfo?.patnrId);
|
||||||
if (patnrId === (selectedPatnrId ?? panelInfo?.patnrId)) {
|
if (patnrId === (selectedPatnrId ?? panelInfo?.patnrId)) {
|
||||||
|
console.log("[QuickMenuItem] Already selected, returning");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("[QuickMenuItem] Switching to patnrId:", patnrId);
|
||||||
const from = "menu";
|
const from = "menu";
|
||||||
const name = panel_names.FEATURED_BRANDS_PANEL;
|
const name = panel_names.FEATURED_BRANDS_PANEL;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import React, { memo, useCallback, useState } from "react";
|
||||||
|
|
||||||
|
import Spotlight from "@enact/spotlight";
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
import SectionTitle from "../../../components/SectionTitle/SectionTitle";
|
||||||
|
import { $L } from "../../../utils/helperMethods";
|
||||||
|
import css from "./ShopByShow.module.less";
|
||||||
|
import ShopByShowList from "./ShopByShowList/ShopByShowList";
|
||||||
|
|
||||||
|
const STRING_CONF = {
|
||||||
|
SHOP_BY_SHOW: "SHOP BY SHOW",
|
||||||
|
};
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ leaveFor: { right: "" }, enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
const ShopByShow = ({
|
||||||
|
brandShopByShowContsList,
|
||||||
|
brandShopByShowContsInfo,
|
||||||
|
handleItemFocus,
|
||||||
|
order,
|
||||||
|
shelfOrder,
|
||||||
|
spotlightId,
|
||||||
|
selectedPatnrId,
|
||||||
|
shelfTitle,
|
||||||
|
}) => {
|
||||||
|
const [firstChk, setFirstChk] = useState(0);
|
||||||
|
|
||||||
|
const _handleItemFocus = useCallback(() => {
|
||||||
|
if (handleItemFocus) handleItemFocus(spotlightId, shelfOrder);
|
||||||
|
|
||||||
|
const c = Spotlight.getCurrent();
|
||||||
|
if (firstChk === 0) {
|
||||||
|
if (c) {
|
||||||
|
let cAriaLabel = c.getAttribute("aria-label");
|
||||||
|
if (cAriaLabel) {
|
||||||
|
cAriaLabel = "shop-by-show, Heading1," + cAriaLabel;
|
||||||
|
c.setAttribute("aria-label", cAriaLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFirstChk(1);
|
||||||
|
} else if (firstChk === 1) {
|
||||||
|
if (c) {
|
||||||
|
let cAriaLabel = c.getAttribute("aria-label");
|
||||||
|
if (cAriaLabel) {
|
||||||
|
const newcAriaLabel = cAriaLabel.replace(
|
||||||
|
"shop-by-show, Heading1,",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
c.setAttribute("aria-label", newcAriaLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [handleItemFocus, firstChk]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
className={css.container}
|
||||||
|
data-shelf-order={order}
|
||||||
|
data-wheel-point
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
>
|
||||||
|
<SectionTitle
|
||||||
|
title={$L(STRING_CONF.SHOP_BY_SHOW)}
|
||||||
|
data-title="shop-by-show"
|
||||||
|
label="shop-by-show Heading 1"
|
||||||
|
/>
|
||||||
|
<ShopByShowList
|
||||||
|
brandShopByShowContsList={brandShopByShowContsList}
|
||||||
|
brandShopByShowContsInfo={brandShopByShowContsInfo}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
shelfOrder={shelfOrder}
|
||||||
|
shelfTitle={shelfTitle}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ShopByShow);
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
@import "../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 58px;
|
||||||
|
|
||||||
|
> h2 {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding-left: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import React, { memo, useCallback } from "react";
|
||||||
|
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
import TItemCardNew, { removeDotAndColon } from "../../../../../components/TItemCard/TItemCard.new";
|
||||||
|
import TVirtualGridList from "../../../../../components/TVirtualGridList/TVirtualGridList";
|
||||||
|
import {
|
||||||
|
LOG_CONTEXT_NAME,
|
||||||
|
LOG_MESSAGE_ID,
|
||||||
|
panel_names,
|
||||||
|
} from "../../../../../utils/Config";
|
||||||
|
import { getTranslate3dValueByDirection } from "../../../../../utils/helperMethods";
|
||||||
|
import Spotlight from "@enact/spotlight";
|
||||||
|
import { pushPanel, updatePanel } from "../../../../../actions/panelActions";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import css from "./ShopByShowContents.module.less";
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ leaveFor: { right: "" }, enterTo: null },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
const ShopByShowContents = memo(({
|
||||||
|
clctId,
|
||||||
|
clctNm,
|
||||||
|
brandProductInfos,
|
||||||
|
contentsIndex,
|
||||||
|
handleItemFocus,
|
||||||
|
selectedPatnrId,
|
||||||
|
spotlightId,
|
||||||
|
shelfOrder,
|
||||||
|
shelfTitle,
|
||||||
|
getScrollTo,
|
||||||
|
}) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const handleClick = useCallback(
|
||||||
|
(patnrId, prdtId) => (e) => {
|
||||||
|
const tItemCard = e.currentTarget;
|
||||||
|
const lastFocusedTarget = Spotlight.getCurrent();
|
||||||
|
const lastFocusedTargetId = lastFocusedTarget?.getAttribute("data-spotlight-id");
|
||||||
|
const xContainer = tItemCard?.parentNode?.parentNode;
|
||||||
|
|
||||||
|
if (lastFocusedTargetId && xContainer) {
|
||||||
|
const section = "shop-by-show";
|
||||||
|
const x = getTranslate3dValueByDirection(xContainer);
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
updatePanel({
|
||||||
|
name: panel_names.FEATURED_BRANDS_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
lastFocusedTargetId,
|
||||||
|
patnrId,
|
||||||
|
section,
|
||||||
|
x,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
pushPanel({
|
||||||
|
name: panel_names.DETAIL_PANEL,
|
||||||
|
panelInfo: { patnrId, prdtId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFocus = useCallback(() => {
|
||||||
|
if (handleItemFocus) {
|
||||||
|
handleItemFocus();
|
||||||
|
}
|
||||||
|
}, [handleItemFocus]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
className={css.container}
|
||||||
|
data-wheel-point
|
||||||
|
spotlightId={`${spotlightId}-${contentsIndex}`}
|
||||||
|
>
|
||||||
|
<h3 data-collection-subtitle={clctNm}>{clctNm}</h3>
|
||||||
|
{brandProductInfos && brandProductInfos.length > 0 && (
|
||||||
|
<TVirtualGridList
|
||||||
|
cbScrollTo={getScrollTo}
|
||||||
|
className={css.tVirtualGridList}
|
||||||
|
dataSize={brandProductInfos.length}
|
||||||
|
direction="horizontal"
|
||||||
|
itemHeight={438}
|
||||||
|
itemWidth={324}
|
||||||
|
spacing={18}
|
||||||
|
renderItem={({ index, ...rest }) => {
|
||||||
|
if (!brandProductInfos || !brandProductInfos[index]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const product = brandProductInfos[index];
|
||||||
|
const {
|
||||||
|
prdtImgUrl,
|
||||||
|
prdtOfferId,
|
||||||
|
patnrId = "21",
|
||||||
|
prdtNm,
|
||||||
|
prdtId,
|
||||||
|
prdtPrice,
|
||||||
|
patncNm,
|
||||||
|
brndNm,
|
||||||
|
lgCatNm,
|
||||||
|
euEnrgLblInfos,
|
||||||
|
} = product;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TItemCardNew
|
||||||
|
catNm={lgCatNm}
|
||||||
|
contextName={LOG_CONTEXT_NAME.FEATURED_BRANDS}
|
||||||
|
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
|
||||||
|
patnerName={patncNm || "Peacock | Shop The Moment"}
|
||||||
|
brandName={brndNm}
|
||||||
|
shelfId={spotlightId}
|
||||||
|
shelfLocation={shelfOrder}
|
||||||
|
shelfTitle={shelfTitle}
|
||||||
|
imageAlt={prdtNm}
|
||||||
|
imageSource={prdtImgUrl}
|
||||||
|
onClick={handleClick(patnrId, prdtId)}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
offerInfo={prdtOfferId}
|
||||||
|
priceInfo={prdtPrice}
|
||||||
|
productId={prdtId}
|
||||||
|
productName={prdtNm}
|
||||||
|
spotlightId={"shop-by-show-spotlightId-" + removeDotAndColon(prdtId)}
|
||||||
|
label={index * 1 + 1 + " of " + brandProductInfos.length}
|
||||||
|
lastLabel=" go to detail, button"
|
||||||
|
euEnrgLblInfos={euEnrgLblInfos}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ShopByShowContents;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
@import "../../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding-left: 60px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
position: relative;
|
||||||
|
.font(@fontFamily: @arialFontBold, @fontSize: 36px);
|
||||||
|
color: @COLOR_GRAY08;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div:nth-child(2) {
|
||||||
|
.flex(@justifyCenter: flex-start);
|
||||||
|
.size(@w: 100%, @h: auto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tVirtualGridList {
|
||||||
|
padding-right: 18px;
|
||||||
|
.size(@h: 438px);
|
||||||
|
|
||||||
|
> div:nth-child(3) {
|
||||||
|
right: -18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
useDispatch,
|
||||||
|
useSelector,
|
||||||
|
} from 'react-redux';
|
||||||
|
|
||||||
|
import { Job } from '@enact/core/util';
|
||||||
|
import SpotlightContainerDecorator
|
||||||
|
from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
|
import {
|
||||||
|
getContainerId,
|
||||||
|
setContainerLastFocusedElement,
|
||||||
|
} from '@enact/spotlight/src/container';
|
||||||
|
|
||||||
|
import { getBrandShopByShow } from '../../../../actions/brandActions';
|
||||||
|
import useScrollTo from '../../../../hooks/useScrollTo';
|
||||||
|
import css from './ShopByShowList.module.less';
|
||||||
|
import ShopByShowNav from './ShopByShowNav/ShopByShowNav';
|
||||||
|
import ShopByShowContents from './ShopByShowContents/ShopByShowContents';
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ leaveFor: { right: "" }, enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function ShopByShowList({
|
||||||
|
brandShopByShowContsList,
|
||||||
|
brandShopByShowContsInfo,
|
||||||
|
handleItemFocus,
|
||||||
|
selectedPatnrId,
|
||||||
|
spotlightId,
|
||||||
|
shelfTitle,
|
||||||
|
shelfOrder,
|
||||||
|
}) {
|
||||||
|
const { getScrollTo, scrollLeft } = useScrollTo();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const panelInfo = useSelector((state) => state.panels.panels[0]?.panelInfo);
|
||||||
|
const scrollLeftJob = useRef(new Job((func) => func(), 0));
|
||||||
|
const [selectedClctId, setSelectedClctId] = useState(null);
|
||||||
|
|
||||||
|
const brandShopByShowClctInfos = brandShopByShowContsInfo?.brandShopByShowClctInfos || [];
|
||||||
|
|
||||||
|
const filteredCollections = selectedClctId
|
||||||
|
? brandShopByShowClctInfos.filter(({ clctId }) => clctId === selectedClctId)
|
||||||
|
: brandShopByShowClctInfos;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (panelInfo?.section !== "shop-by-show" || !panelInfo?.x) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollLeftJobValue = scrollLeftJob.current;
|
||||||
|
const { x } = panelInfo;
|
||||||
|
|
||||||
|
scrollLeftJobValue.start(() => scrollLeft({ x }));
|
||||||
|
|
||||||
|
return () => scrollLeftJobValue.stop();
|
||||||
|
}, [panelInfo, scrollLeft]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const containerId = "shop-by-show-list-id";
|
||||||
|
const container = document.getElementById(containerId);
|
||||||
|
|
||||||
|
if (container) {
|
||||||
|
const childrenId = getContainerId(container?.children[0].children[0]);
|
||||||
|
|
||||||
|
if (childrenId) {
|
||||||
|
setContainerLastFocusedElement(null, [containerId, childrenId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollLeft();
|
||||||
|
}, [scrollLeft, selectedPatnrId]);
|
||||||
|
|
||||||
|
const handleTabClick = useCallback((contsId) => {
|
||||||
|
dispatch(getBrandShopByShow({ patnrId: selectedPatnrId, contsId }));
|
||||||
|
}, [selectedPatnrId, dispatch]);
|
||||||
|
|
||||||
|
const _handleItemFocus = useCallback(() => {
|
||||||
|
if (handleItemFocus) {
|
||||||
|
handleItemFocus();
|
||||||
|
}
|
||||||
|
}, [handleItemFocus]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
className={css.container}
|
||||||
|
id={"shop-by-show-list-id"}
|
||||||
|
spotlightId={"shop-by-show-list-id"}
|
||||||
|
>
|
||||||
|
<div className={css.tabsContainer}>
|
||||||
|
{brandShopByShowContsList && brandShopByShowContsList.map((item) => (
|
||||||
|
<button
|
||||||
|
key={item.contsId}
|
||||||
|
className={`${css.tabButton} ${brandShopByShowContsInfo?.contsId === item.contsId ? css.active : ''}`}
|
||||||
|
onClick={() => handleTabClick(item.contsId)}
|
||||||
|
onMouseDown={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
{item.contsNm}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ShopByShowNav
|
||||||
|
brandShopByShowClctInfos={brandShopByShowClctInfos}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
selectedClctId={selectedClctId}
|
||||||
|
setSelectedClctId={setSelectedClctId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{filteredCollections.map((collection, collIdx) => (
|
||||||
|
<ShopByShowContents
|
||||||
|
key={`${spotlightId}-${collIdx}`}
|
||||||
|
clctId={collection.clctId}
|
||||||
|
clctNm={collection.clctNm}
|
||||||
|
brandProductInfos={collection.brandProductInfos}
|
||||||
|
contentsIndex={collIdx}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
selectedPatnrId={selectedPatnrId}
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
shelfOrder={shelfOrder}
|
||||||
|
shelfTitle={shelfTitle}
|
||||||
|
getScrollTo={getScrollTo}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
@import "../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabsContainer {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 60px 24px 60px;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: #ccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabButton {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #000;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #000;
|
||||||
|
border-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import React, { memo, useCallback } from "react";
|
||||||
|
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
import TButton, { TYPES } from "../../../../../components/TButton/TButton";
|
||||||
|
import TScroller from "../../../../../components/TScroller/TScroller";
|
||||||
|
import useScrollTo from "../../../../../hooks/useScrollTo";
|
||||||
|
import css from "./ShopByShowNav.module.less";
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ leaveFor: { right: "" }, enterTo: "last-focused" },
|
||||||
|
"nav"
|
||||||
|
);
|
||||||
|
|
||||||
|
const STRING_CONF = {
|
||||||
|
ALL: "ALL",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(function ShopByShowNav({
|
||||||
|
brandShopByShowClctInfos,
|
||||||
|
handleItemFocus,
|
||||||
|
selectedClctId,
|
||||||
|
setSelectedClctId,
|
||||||
|
}) {
|
||||||
|
const { getScrollTo, scrollLeft } = useScrollTo();
|
||||||
|
|
||||||
|
const handleClick = useCallback(
|
||||||
|
(clctId, clctNm) => () => {
|
||||||
|
setSelectedClctId(clctId);
|
||||||
|
},
|
||||||
|
[setSelectedClctId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFocus = useCallback(() => {
|
||||||
|
if (handleItemFocus) {
|
||||||
|
handleItemFocus();
|
||||||
|
}
|
||||||
|
}, [handleItemFocus]);
|
||||||
|
|
||||||
|
const selectedText = !selectedClctId ? "Selected " : "";
|
||||||
|
const allLabelText = selectedText + "ALL 1 of " + (brandShopByShowClctInfos?.length + 1 || 1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className={css.nav} id="shop-by-show-nav-id" spotlightId="shop-by-show-nav-id">
|
||||||
|
<TScroller cbScrollTo={getScrollTo} direction="horizontal" noScrollByWheel>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<TButton
|
||||||
|
className={!selectedClctId && css.selected}
|
||||||
|
onClick={handleClick(null, null)}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
selected={!selectedClctId}
|
||||||
|
type={TYPES.oneDepthCategory}
|
||||||
|
ariaLabel={allLabelText}
|
||||||
|
>
|
||||||
|
ALL
|
||||||
|
</TButton>
|
||||||
|
</li>
|
||||||
|
{brandShopByShowClctInfos &&
|
||||||
|
brandShopByShowClctInfos.map(({ clctId, clctNm }, index) => (
|
||||||
|
<li key={"shop-by-show-clct-" + index}>
|
||||||
|
<TButton
|
||||||
|
className={selectedClctId && selectedClctId === clctId && css.selected}
|
||||||
|
onClick={handleClick(clctId, clctNm)}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
selected={selectedClctId && selectedClctId === clctId}
|
||||||
|
type={TYPES.oneDepthCategory}
|
||||||
|
ariaLabel={
|
||||||
|
selectedClctId && selectedClctId === clctId
|
||||||
|
? "Selected " + clctNm + " " + (index * 1 + 2) + " of " + (brandShopByShowClctInfos.length + 1)
|
||||||
|
: "" + clctNm + " " + (index * 1 + 2) + " of " + (brandShopByShowClctInfos.length + 1)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{clctNm}
|
||||||
|
</TButton>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</TScroller>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
@import "../../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
position: relative;
|
||||||
|
.size(@w: 100%, @h: 162px);
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding-right: 1px;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
> div:nth-child(1) {
|
||||||
|
.size(@w: inherit, @h: inherit);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
.size(@w: 100%, @h: 144px);
|
||||||
|
background-color: #ddd;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: inherit;
|
||||||
|
padding-left: 60px;
|
||||||
|
border-bottom: 18px solid transparent;
|
||||||
|
|
||||||
|
li {
|
||||||
|
flex: none;
|
||||||
|
margin-right: 12px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -62px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
.size(@w: 0, @h: 0);
|
||||||
|
border-top: 18px solid #ddd;
|
||||||
|
border-right: 18px solid transparent;
|
||||||
|
border-bottom: 18px solid transparent;
|
||||||
|
border-left: 18px solid transparent;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user