Skip to content

Commit

Permalink
[FE] 탭 이동 버튼과 드래그 이동을 구현한다. (#948)
Browse files Browse the repository at this point in the history
  • Loading branch information
skiende74 authored Nov 7, 2024
1 parent 0fc4fb5 commit 5da58d8
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import Divider from '@/components/_common/Divider/Divider';
import Layout from '@/components/_common/layout/Layout';
import { useTabContext } from '@/components/_common/Tabs/TabContext';
import ChecklistQuestionItem from '@/components/NewChecklist/ChecklistQuestion/ChecklistQuestion';
import MoveNextButton from '@/components/NewChecklist/MoveNextButton';
import useInitialChecklist from '@/hooks/useInitialChecklist';
import useChecklistStore from '@/store/useChecklistStore';
import { flexColumn } from '@/styles/common';
import { flexCenter, flexColumn } from '@/styles/common';
import theme from '@/styles/theme';
import { ChecklistQuestion } from '@/types/checklist';

Expand Down Expand Up @@ -38,6 +39,8 @@ const ChecklistQuestionTemplate = () => {
);
})}
</S.ContentBox>

<MoveNextButton marginTop="2rem" marginBottom="4rem" />
</Layout>
);
};
Expand All @@ -47,7 +50,7 @@ export default ChecklistQuestionTemplate;
const S = {
ContentBox: styled.div`
${flexColumn}
margin-bottom: 2rem;
${flexCenter}
border-radius: 0.8rem;
background-color: ${({ theme }) => theme.palette.white};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { useMemo } from 'react';

import ChecklistTabSuspense from '@/components/_common/errorBoundary/ChecklistTabSuspense';
import { useTabContext } from '@/components/_common/Tabs/TabContext';
import Tabs from '@/components/_common/Tabs/Tabs';
import { DefaultChecklistTabsNames } from '@/constants/tabs';
import useInitialChecklist from '@/hooks/useInitialChecklist';
import useMouseDrag from '@/hooks/useMouseDrag';
import useTabs from '@/hooks/useTabs';
import useChecklistStore from '@/store/useChecklistStore';
import isSameCategory from '@/utils/isSameCategory';
Expand All @@ -11,6 +14,20 @@ const NewChecklistTab = () => {
const { data: checklist, isSuccess, isLoading } = useInitialChecklist();
const checklistStore = useChecklistStore(state => state.checklistCategoryQnA);
const { getTabsForChecklist } = useTabs();
const { setCurrentTabId } = useTabContext();

useMouseDrag((S, E) => {
const DRAG_THRESHOLD = 100;
const TAB_COUNT = DefaultChecklistTabsNames.length;
const remainOp = (a: number, b: number) => (((a % b) + b + 1) % b) - 1; // 나머지연산자. -1부터 시작하므로 +1 -1
setCurrentTabId(tabId => {
const isLeftDrag = E.x - S.x > DRAG_THRESHOLD;
const isRightDrag = S.x - E.x > DRAG_THRESHOLD;
if (isLeftDrag) return remainOp(tabId - 1, TAB_COUNT);
if (isRightDrag) return remainOp(tabId + 1, TAB_COUNT);
return tabId;
});
});

const categoryTabs = useMemo(() => {
if (isSuccess && isSameCategory(checklist, checklistStore)) {
Expand Down
57 changes: 57 additions & 0 deletions frontend/src/components/NewChecklist/MoveNextButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import styled from '@emotion/styled';

import { useTabContext } from '@/components/_common/Tabs/TabContext';
import { DefaultChecklistTabsNames } from '@/constants/tabs';
import { flexRow } from '@/styles/common';
import theme from '@/styles/theme';

const TAB_COUNT = DefaultChecklistTabsNames.length;
interface Props {
marginTop?: string;
marginBottom?: string;
}
const MoveNextButton = ({ marginTop = '0', marginBottom = '0' }: Props) => {
const { setCurrentTabId } = useTabContext();

const handleClickPrev = () => setCurrentTabId(tabId => (tabId % TAB_COUNT) - 1);
const handleClickNext = () => setCurrentTabId(tabId => ((tabId + 2) % TAB_COUNT) - 1);
return (
<S.ContentBox marginTop={marginTop} marginBottom={marginBottom}>
<S.Button onClick={handleClickPrev}>
<S.Text color={theme.palette.grey400}>{'< '}</S.Text>
{'이전으로 이동'}
</S.Button>
<S.Button onClick={handleClickNext}>
{'다음으로 이동'}
<S.Text color={theme.palette.grey400}>{' >'}</S.Text>
</S.Button>
</S.ContentBox>
);
};

export default MoveNextButton;

const S = {
Text: styled.span<{ color: string }>`
color: ${({ color }) => color};
font-weight: ${({ theme }) => theme.text.weight.bold};
`,
Button: styled.button`
margin: 10px 0;
padding: 0 15px;
background-color: ${({ theme }) => theme.palette.grey200};
font-weight: ${({ theme }) => theme.text.weight.medium};
font-size: ${({ theme }) => theme.text.size.small};
line-height: 2.5;
border-radius: 10px;
`,
ContentBox: styled.div<{ marginTop: string; marginBottom: string }>`
${flexRow}
justify-content: space-around;
margin-top: ${({ marginTop }) => marginTop};
margin-bottom: ${({ marginBottom }) => marginBottom};
border-radius: 0.8rem;
`,
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { ErrorBoundary } from 'react-error-boundary';

import Layout from '@/components/_common/layout/Layout';
import FocusTrap from '@/components/_common/Modal/FocusTrap/FocusTrap';
import MoveNextButton from '@/components/NewChecklist/MoveNextButton';
import Address from '@/components/NewChecklist/NewRoomInfoForm/Address';
import DepositAndRent from '@/components/NewChecklist/NewRoomInfoForm/DepositAndRent';
import IncludedMaintenances from '@/components/NewChecklist/NewRoomInfoForm/IncludedMaintenances';
Expand All @@ -25,25 +25,24 @@ const RoomInfoTemplate = () => {
};

return (
<Layout withHeader withTab>
<FocusTrap>
<S.Container onBlur={handleTrackInput}>
<ErrorBoundary FallbackComponent={RoomNameNoDefault}>
<RoomName />
</ErrorBoundary>
<Address />
<NearSubwayStations />
<DepositAndRent />
<MaintenanceFee />
<IncludedMaintenances />
<RoomFloor />
<RoomStructure />
<RoomSize />
<RoomContractTerm />
<OccupancyMonth />
<RealEstate />
</S.Container>
</FocusTrap>
<Layout withHeader withTab withFooter>
<S.Container onBlur={handleTrackInput}>
<ErrorBoundary FallbackComponent={RoomNameNoDefault}>
<RoomName />
</ErrorBoundary>
<Address />
<NearSubwayStations />
<DepositAndRent />
<MaintenanceFee />
<IncludedMaintenances />
<RoomFloor />
<RoomStructure />
<RoomSize />
<RoomContractTerm />
<OccupancyMonth />
<RealEstate />
<MoveNextButton marginBottom="1rem" />
</S.Container>
</Layout>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import styled from '@emotion/styled';

import Layout from '@/components/_common/layout/Layout';
import TipBox from '@/components/_common/TipBox/TipBox';
import MoveNextButton from '@/components/NewChecklist/MoveNextButton';
import OptionAllSelectBox from '@/components/NewChecklist/Option/OptionAllSelectBox';
import { OptionList } from '@/components/NewChecklist/Option/OptionList';
import { flexCenter, flexColumn, flexRow, flexSpaceBetween, title4 } from '@/styles/common';
Expand All @@ -17,6 +18,7 @@ const OptionTemplate = () => {
<OptionList />
</S.OptionBox>
</S.InnerBox>
<MoveNextButton marginTop="2rem" marginBottom="4rem" />
</Layout>
);
};
Expand Down
47 changes: 47 additions & 0 deletions frontend/src/hooks/useMouseDrag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useEffect, useState } from 'react';

interface MousePosition {
x: number;
y: number;
}
type Handler = (start: MousePosition, end: MousePosition) => void;

const useMouseDrag = (handler: Handler) => {
const [startPosition, setStartPosition] = useState<MousePosition | null>(null);

useEffect(() => {
const pointerdownListener = (e: MouseEvent) => {
setStartPosition({ x: e.clientX, y: e.clientY });
};
const touchStartListener = (e: TouchEvent) => {
setStartPosition({ x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientX });
};

const pointerupListener = (e: MouseEvent) => {
const endPosition = { x: e.clientX, y: e.clientY };
if (!startPosition) return;
handler(startPosition, endPosition);
setStartPosition(null);
};
const touchEndListener = (e: TouchEvent) => {
const endPosition = { x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY };
if (!startPosition) return;
handler(startPosition, endPosition);
setStartPosition(null);
};

window.addEventListener('mousedown', pointerdownListener);
window.addEventListener('touchstart', touchStartListener);
window.addEventListener('mouseup', pointerupListener);
window.addEventListener('touchend', touchEndListener);

return () => {
window.removeEventListener('mousedown', pointerdownListener);
window.removeEventListener('mouseup', pointerupListener);
window.removeEventListener('touchstart', touchStartListener);
window.removeEventListener('touchend', touchEndListener);
};
}, [startPosition, handler]);
};

export default useMouseDrag;
2 changes: 1 addition & 1 deletion frontend/src/pages/NewChecklistPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ErrorBoundary } from 'react-error-boundary';
import { useNavigate } from 'react-router-dom';

import { useStore } from 'zustand';

import Button from '@/components/_common/Button/Button';
import ChecklistTabFallback from '@/components/_common/errorBoundary/ChecklistTabFallback';
import Header from '@/components/_common/Header/Header';
Expand Down

0 comments on commit 5da58d8

Please sign in to comment.