Skip to content

Commit

Permalink
feat: #330 TimePicker 수정 UI/UX 반영
Browse files Browse the repository at this point in the history
  • Loading branch information
leeminhee119 committed Sep 10, 2024
1 parent f0f53fa commit 376ecd6
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 20 deletions.
2 changes: 1 addition & 1 deletion apps/web/src/assets/svgs/calendar/ic_next_chevron.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion apps/web/src/assets/svgs/calendar/ic_prev_chevron.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions apps/web/src/component/common/dateTimePicker/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ReactCalendar from "react-calendar";
import type { CalendarProps as ReactCalendarProps } from "react-calendar";

import { Icon } from "@/component/common/Icon";
import { DESIGN_TOKEN_COLOR } from "@/style/designTokens";

type CalendarProps = ReactCalendarProps;

Expand All @@ -10,8 +11,8 @@ export function Calendar({ ...props }: CalendarProps) {
<ReactCalendar
calendarType={"gregory"}
formatDay={(_, date) => new Intl.DateTimeFormat("en", { day: "numeric" }).format(date)}
prevLabel={<Icon icon={"ic_prev_chevron"} style={{ cursor: "inherit" }} />}
nextLabel={<Icon icon={"ic_next_chevron"} style={{ cursor: "inherit" }} />}
prevLabel={<Icon icon={"ic_prev_chevron"} color={DESIGN_TOKEN_COLOR.gray900} style={{ cursor: "inherit" }} />}
nextLabel={<Icon icon={"ic_next_chevron"} color={DESIGN_TOKEN_COLOR.gray900} style={{ cursor: "inherit" }} />}
{...props}
/>
);
Expand Down
84 changes: 72 additions & 12 deletions apps/web/src/component/common/dateTimePicker/TimePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { css } from "@emotion/react";
import { forwardRef } from "react";
import { forwardRef, useEffect, useRef } from "react";

import { TIME_24 } from "@/component/common/dateTimePicker/time.const";
import { Radio, RadioButtonGroup } from "@/component/common/radioButton";
Expand All @@ -12,33 +12,93 @@ type TimePickerProps = {
};
export const TimePicker = forwardRef<HTMLDivElement, TimePickerProps>(function ({ radioControl }, ref) {
const { curTab, tabs, selectTab } = useTabs(["오전", "오후"] as const);
const radioButtonsContainerRef = useRef<HTMLDivElement>(null);

const pmFirstItemRef = useRef<HTMLLabelElement>(null);
const amFirstItemRef = useRef<HTMLLabelElement>(null);

/**
* 탭을 클릭해 scrollIntoView가 작동 중인지에 대한 상태
*/
const isScrollingIntoView = useRef(false);

useEffect(() => {
const checkPmInView = () => {
if (isScrollingIntoView.current) {
return;
}

const pmFirstClientRect = pmFirstItemRef.current?.getBoundingClientRect();
if (pmFirstClientRect && pmFirstClientRect.x < 200) {
selectTab("오후");
} else {
selectTab("오전");
}
};

const containerRef = radioButtonsContainerRef.current;
containerRef?.addEventListener("scroll", checkPmInView);

return () => {
containerRef?.removeEventListener("scroll", checkPmInView);
};
}, [selectTab]);

return (
<div
ref={ref}
css={css`
display: flex;
flex-direction: column;
gap: 2.4rem;
`}
>
<AmPmTabs tabs={tabs} curTab={curTab} selectTab={selectTab} />
<RadioButtonGroup isChecked={radioControl.isTimeChecked} onChange={radioControl.onTimeChange} radioName={"회고 마감 시간"} ref={ref}>
{curTab === "오전" &&
TIME_24.slice(0, 12).map((time, index) => {
<AmPmTabs
tabs={tabs}
curTab={curTab}
selectTab={(tab) => {
selectTab(tab);
isScrollingIntoView.current = true;

if (tab === "오후") {
pmFirstItemRef.current?.scrollIntoView({ behavior: "smooth", inline: "start" });
}
if (tab === "오전") {
amFirstItemRef.current?.scrollIntoView({ behavior: "smooth", inline: "start" });
}

setTimeout(() => {
isScrollingIntoView.current = false;
}, 1000);
}}
/>
<div
css={css`
display: flex;
`}
>
<RadioButtonGroup
isChecked={radioControl.isTimeChecked}
onChange={radioControl.onTimeChange}
radioName={"회고 마감 시간"}
ref={radioButtonsContainerRef}
>
{TIME_24.slice(0, 13).map((time, index) => {
return (
<Radio key={index} value={time}>
{time}
<Radio ref={index === 0 ? amFirstItemRef : null} value={time} key={index}>
<span>{time}</span>
</Radio>
);
})}
{curTab === "오후" &&
TIME_24.slice(12).map((time, index) => {
{TIME_24.slice(13).map((time, index) => {
return (
<Radio key={index} value={time}>
{`${Number(time.split(":")[0]) - 12}:00`}
<Radio ref={index === 0 ? pmFirstItemRef : null} value={time} key={index}>
<span>{time}</span>
</Radio>
);
})}
</RadioButtonGroup>
</RadioButtonGroup>
</div>
</div>
);
});
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/component/common/dateTimePicker/time.const.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const TIME_24 = Array.from({ length: 24 }, (_, i) => `${i + 1}:00`);
export const TIME_24 = Array.from({ length: 25 }, (_, i) => `${i === 0 ? "00" : i}:00`);
9 changes: 6 additions & 3 deletions apps/web/src/component/common/radioButton/Radio.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { css } from "@emotion/react";
import { useContext } from "react";
import { forwardRef, useContext } from "react";

import { RadioContext } from "./RadioButtonGroup";

Expand All @@ -11,10 +11,11 @@ type RadioProps = {
children: React.ReactNode;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, "checked">;

export function Radio({ value, children, ...props }: RadioProps) {
export const Radio = forwardRef<HTMLLabelElement, RadioProps>(function ({ value, children, ...props }, ref) {
const radioContext = useContext(RadioContext);
return (
<label
ref={ref}
htmlFor={value}
css={css`
font-weight: 600;
Expand Down Expand Up @@ -44,4 +45,6 @@ export function Radio({ value, children, ...props }: RadioProps) {
/>
</label>
);
}
});

Radio.displayName = "Radio";

0 comments on commit 376ecd6

Please sign in to comment.