import { memoize } from 'lodash';
import React, {
RefObject,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
import useStores from 'stores/useStores';
import NavigationMenu from './NavigationMenu';
import css from './ScrollMenuNavigation.module.scss';
import Section from './Section';
export type curationInterests = {
width: number;
height: number;
url: string;
text: string[];
n: number;
};
export interface CurationsInterface {
id: number;
title: string;
description: string;
curationLocation: string;
curationType: string;
itemType: 'BANNER' | 'INTEREST' | 'PRODUCT';
offsetName: string;
viewType: 'VERTICAL' | 'HORIZONTAL';
curationProducts: any[];
curationBanners: {
url: string;
width: number;
height: number;
link: {
web: string;
aos: string;
ios: string;
};
}[];
curationReviews: any[];
curationInterests: curationInterests[];
}
export interface ItemListInterface extends CurationsInterface {
verticalRef: React.RefObject<HTMLDivElement>;
horizonRef: React.RefObject<HTMLDivElement>;
}
export type horizontalListType = {
curationId: number;
text: string;
horizonRef: RefObject<HTMLDivElement>;
};
export type verticalListType = Omit<ItemListInterface, 'horizonRef'>;
interface ScrollMenuNavigationProps {
gapTop: number;
top: string;
curations: CurationsInterface[];
offsets: {
curationId: number;
text: string;
}[];
}
export interface SliderRef extends HTMLDivElement {
sliderCurrentPage: number;
setSliderCurrentPage: React.Dispatch<React.SetStateAction<number>>;
}
const ScrollMenuNavigation = ({
gapTop = 0,
top = '6%',
curations,
offsets,
}: ScrollMenuNavigationProps) => {
const [selectedId, setSelectedId] = useState(0);
const [isClickNavMenu, setIsClickNavMenu] = useState(false);
const [marginBottom, setMarginBottom] = useState(0);
const { layout } = useStores();
const sliderRef = useRef<SliderRef>(null);
const containerRef = useRef<HTMLDivElement>(null);
const getVerticalListHandler = memoize((curations) => {
return curations.map((item: any) => ({
...item,
verticalRef: useRef<HTMLDivElement>(null),
}));
});
const verticalList: verticalListType[] = getVerticalListHandler(curations);
const verticlaRefListHandler = memoize((verticalList: verticalListType[]) => {
return verticalList.map((item) => item.verticalRef);
});
const verticalRefList = verticlaRefListHandler(verticalList);
const horizontalList: horizontalListType[] = offsets.map((item) => ({
...item,
horizonRef: useRef<HTMLDivElement>(null),
}));
const findMarginBottom = () => {
const lastVerticalRef =
verticalList[verticalList.length - 1]?.verticalRef?.current;
const containerRefCurrent = containerRef.current;
if (
lastVerticalRef &&
containerRefCurrent &&
offsets.length > 0 &&
lastVerticalRef?.offsetHeight > 0
) {
if (lastVerticalRef.offsetHeight < window.innerHeight) {
const computeMarginBottom =
window.innerHeight - lastVerticalRef.offsetHeight - gapTop - 110;
setMarginBottom(computeMarginBottom);
}
}
};
const naviMenuClickHandler = (curationId: number) => {
const id = Number(curationId);
setSelectedId(id);
setIsClickNavMenu(true);
const getVercitalObject = verticalRefList.find((item) => {
return Number(item.current?.id) === id;
});
if (getVercitalObject?.current) {
window.scrollTo({
top: getVercitalObject?.current.offsetTop - gapTop,
behavior: 'smooth',
});
}
const horizontalMenuCurrent = horizontalList.find(
(item) => item.curationId === id
)?.horizonRef.current;
const sliderCurrent = sliderRef.current;
if (horizontalMenuCurrent && sliderCurrent) {
const offsetLeft = horizontalMenuCurrent.offsetLeft;
const offsetWeight = horizontalMenuCurrent.clientWidth * 0.5;
const sliderWidthWeight = sliderCurrent.offsetWidth * 0.5;
const targetLeft = offsetLeft + offsetWeight - sliderWidthWeight;
if (targetLeft !== sliderCurrent.scrollLeft) {
sliderCurrent.scrollTo({
left: targetLeft,
behavior: 'smooth',
});
}
}
};
useEffect(() => {
const scrollHandler = () => {
if (
window.scrollY === 0 &&
window.scrollY + window.innerHeight ===
document.scrollingElement?.scrollHeight
) {
const endId = horizontalList[horizontalList.length - 1]?.curationId;
const findVerticalItem = verticalList.find((item) => item.id === endId);
if (findVerticalItem) {
return setSelectedId(Number(findVerticalItem.id));
} else {
if (!isClickNavMenu) {
return setSelectedId(0);
}
}
}
if (window.scrollY === 0) {
if (!isClickNavMenu) {
setSelectedId(0);
if (sliderRef?.current?.setSliderCurrentPage) {
sliderRef?.current?.setSliderCurrentPage(0);
}
return sliderRef.current?.scrollTo({ left: 0 });
}
}
if (verticalList[0].verticalRef.current) {
verticalList.forEach((section) => {
if (section.verticalRef.current && sliderRef.current) {
const sectionCurrent = section.verticalRef.current;
const sectionAreaStart = sectionCurrent.offsetTop;
const sctionAreaEnd =
sectionCurrent.offsetTop + sectionCurrent.offsetHeight;
if (
sectionAreaStart <= window.scrollY + gapTop &&
window.scrollY + gapTop <= sctionAreaEnd
) {
if (!isClickNavMenu) {
if (Number(section.id) !== selectedId) {
setSelectedId(Number(section.id));
}
}
}
}
});
}
};
scrollHandler();
window.addEventListener('scroll', scrollHandler);
return () => {
window.removeEventListener('scroll', scrollHandler);
};
}, [verticalList, isClickNavMenu]);
useEffect(() => {
const mouseWheelEvent = () => {
if (isClickNavMenu) {
setIsClickNavMenu(false);
}
};
window.addEventListener('touchmove', mouseWheelEvent);
window.addEventListener('wheel', mouseWheelEvent);
return () => {
window.removeEventListener('touchmove', mouseWheelEvent);
window.removeEventListener('wheel', mouseWheelEvent);
};
}, [isClickNavMenu]);
useLayoutEffect(() => {
const selectedIdEvent = () => {
if (selectedId !== 0) {
const horizontalMenuCurrent = horizontalList.find(
(item) => item.curationId === selectedId
)?.horizonRef.current;
const sliderCurrent = sliderRef.current;
if (horizontalMenuCurrent && sliderCurrent) {
const offsetLeft = horizontalMenuCurrent.offsetLeft;
const offsetWeight = horizontalMenuCurrent.clientWidth * 0.5;
const sliderWidthWeight = sliderCurrent.offsetWidth * 0.5;
const targetLeft = offsetLeft + offsetWeight - sliderWidthWeight;
if (targetLeft !== sliderCurrent.scrollLeft && !isClickNavMenu) {
sliderCurrent.scrollTo({
left: targetLeft,
});
}
if (sliderRef?.current?.setSliderCurrentPage) {
const totalPage = Math.floor(
sliderCurrent.scrollWidth / sliderCurrent.offsetWidth
);
const pagePerWidth = [];
for (let i = 0; i < totalPage; i++) {
pagePerWidth.push((i + 1) * sliderCurrent.offsetWidth);
}
let currentPageIndex = pagePerWidth.findIndex(
(item) => item >= horizontalMenuCurrent.offsetLeft
);
if (
sliderCurrent.scrollWidth <=
horizontalMenuCurrent.offsetLeft +
horizontalMenuCurrent.clientWidth
) {
currentPageIndex = totalPage;
}
sliderRef.current?.setSliderCurrentPage(currentPageIndex);
}
}
}
};
selectedIdEvent();
}, [selectedId, isClickNavMenu]);
useLayoutEffect(() => {
layout.seenCurationScrollTop();
layout.isGoToScrollToTopHandler(false);
}, []);
return (
<div ref={containerRef} className={css['ScrollMenuNavigationWrap']}>
{offsets.length > 0 ? (
<NavigationMenu
horizontalList={horizontalList}
selectedId={selectedId}
naviMenuClickHandler={naviMenuClickHandler}
sliderRef={sliderRef}
top={top}
/>
) : null}
<div style={{ marginBottom: marginBottom }}>
{verticalList.length > 0
? verticalList.map((data, index) => {
return (
<Section
index={index}
key={data.id}
ref={data.verticalRef}
data={data}
/>
);
})
: null}
</div>
</div>
);
};
export default ScrollMenuNavigation;
import React, { forwardRef } from 'react';
import HorizontalItems from 'template/Home/MainCuration/HorizontalItems/HorizontalItems';
import VerticalItems from 'template/Home/MainCuration/VerticalItems/VerticalItems';
import { verticalListType } from '..';
import BannerSection from './BannerSection';
import Interests from './Interests/Interests';
import css from './Section.module.scss';
interface SectionProps {
data: verticalListType;
index: number;
}
const Section = forwardRef<HTMLDivElement, SectionProps>(
({ data, index }, ref) => {
const { id, itemType, curationBanners, curationInterests, viewType } = data;
return (
<div className={css['sectionWrap']} id={String(id)} ref={ref}>
{itemType === 'BANNER' && curationBanners && (
<BannerSection index={index} curationBanners={curationBanners} />
)}
{itemType === 'PRODUCT' && viewType === 'VERTICAL' && (
<div style={{ marginBottom: 50 }}>
<VerticalItems deals={data} />
</div>
)}
{itemType === 'PRODUCT' && viewType === 'HORIZONTAL' && (
<div style={{ marginBottom: 50 }}>
<HorizontalItems deals={data} />
</div>
)}
{itemType === 'INTEREST' && (
<Interests curationInterests={curationInterests} />
)}
</div>
);
}
);
export default React.memo(Section);