From f0f53fae8cead521395b4ff352769004444a78a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Tue, 10 Sep 2024 00:51:20 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20#330=20=ED=9A=8C=EA=B3=A0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=A7=88=EA=B0=90=EC=9D=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20UI/UX=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/common/radioButton/Radio.tsx | 4 +- .../common/radioButton/RadioButtonGroup.tsx | 9 ++--- .../retrospectCreate/steps/DueDate.tsx | 37 ++++++++++++++----- apps/web/src/hooks/useRadioButton.ts | 1 + apps/web/src/types/retrospectCreate/index.ts | 2 +- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/apps/web/src/component/common/radioButton/Radio.tsx b/apps/web/src/component/common/radioButton/Radio.tsx index 8395fdba..bd22d8d8 100644 --- a/apps/web/src/component/common/radioButton/Radio.tsx +++ b/apps/web/src/component/common/radioButton/Radio.tsx @@ -18,9 +18,7 @@ export function Radio({ value, children, ...props }: RadioProps) { htmlFor={value} css={css` font-weight: 600; - display: flex; - justify-content: center; - align-items: center; + width: fit-content; padding: 1.2rem 1.6rem; border-radius: 0.6rem; background-color: ${radioContext?.isChecked(value) ? DESIGN_SYSTEM_COLOR.theme3 : DESIGN_SYSTEM_COLOR.lightGrey2}; diff --git a/apps/web/src/component/common/radioButton/RadioButtonGroup.tsx b/apps/web/src/component/common/radioButton/RadioButtonGroup.tsx index a9c63606..39b3e211 100644 --- a/apps/web/src/component/common/radioButton/RadioButtonGroup.tsx +++ b/apps/web/src/component/common/radioButton/RadioButtonGroup.tsx @@ -1,11 +1,10 @@ -import { css, Interpolation, Theme } from "@emotion/react"; +import { css, SerializedStyles } from "@emotion/react"; import { createContext, forwardRef } from "react"; export type RadioContextState = { radioName: string; isChecked: (value: string) => boolean; onChange: (value: string) => void; - radioStyles?: Interpolation; }; export const RadioContext = createContext(undefined); @@ -14,11 +13,11 @@ type RadioButtonGroupProps = { children: React.ReactNode; direction?: "row" | "column"; gap?: number; - styles?: Interpolation; + styles?: SerializedStyles; } & RadioContextState; export const RadioButtonGroup = forwardRef(function ( - { children, styles, gap = 0.8, direction = "row", ...props }, + { children, gap = 0.8, direction = "row", styles, radioName, isChecked, onChange }, ref, ) { return ( @@ -34,7 +33,7 @@ export const RadioButtonGroup = forwardRef - {children} + {children} ); }); diff --git a/apps/web/src/component/retrospectCreate/steps/DueDate.tsx b/apps/web/src/component/retrospectCreate/steps/DueDate.tsx index 941cf847..956fde56 100644 --- a/apps/web/src/component/retrospectCreate/steps/DueDate.tsx +++ b/apps/web/src/component/retrospectCreate/steps/DueDate.tsx @@ -1,3 +1,4 @@ +import { css } from "@emotion/react"; import { useSetAtom } from "jotai"; import { useContext, useState } from "react"; @@ -5,18 +6,18 @@ import { RetrospectCreateContext } from "@/app/retrospectCreate/RetrospectCreate import { ButtonProvider } from "@/component/common/button"; import { Header } from "@/component/common/header"; import { DateTimeInput } from "@/component/common/input/DateTimeInput"; +import { Radio, RadioButtonGroup } from "@/component/common/radioButton"; import { Spacing } from "@/component/common/Spacing"; +import { useRadioButton } from "@/hooks/useRadioButton"; import { retrospectCreateAtom } from "@/store/retrospect/retrospectCreate"; export function DueDate() { const { goNext, isMutatePending } = useContext(RetrospectCreateContext); const setRetroCreateData = useSetAtom(retrospectCreateAtom); const [selectedDateTime, setSelectedDateTime] = useState(); + const { selectedValue, isChecked, onChange } = useRadioButton(); const handleDataSave = () => { - if (!selectedDateTime) { - return; - } setRetroCreateData((prev) => ({ ...prev, deadline: selectedDateTime })); }; @@ -29,13 +30,31 @@ export function DueDate() { <>
- { - setSelectedDateTime(value); - }} - /> + + 마감일 미지정 + 마감일 지정 + + {selectedValue === "has-duedate-pos" && ( + { + setSelectedDateTime(value); + }} + /> + )} - + 다음 diff --git a/apps/web/src/hooks/useRadioButton.ts b/apps/web/src/hooks/useRadioButton.ts index 934be9f4..eff25d20 100644 --- a/apps/web/src/hooks/useRadioButton.ts +++ b/apps/web/src/hooks/useRadioButton.ts @@ -1,6 +1,7 @@ import { useState } from "react"; export const useRadioButton = (defaultValue?: string) => { + //FIXME - value를 제네릭 타입으로 수정하기 const [selectedValue, setSelectedValue] = useState(defaultValue); const isChecked = (value: string) => selectedValue === value; const onChange = (value: string) => setSelectedValue(value); diff --git a/apps/web/src/types/retrospectCreate/index.ts b/apps/web/src/types/retrospectCreate/index.ts index be851f29..6cd278ff 100644 --- a/apps/web/src/types/retrospectCreate/index.ts +++ b/apps/web/src/types/retrospectCreate/index.ts @@ -11,7 +11,7 @@ export type RetrospectCreateReq = { title: string; introduction?: string; questions: Questions; - deadline: string; + deadline?: string; /** * 기본 템플릿을 수정한 경우 true */ From 376ecd621ae08352fcaad4e9b653889cd06ac92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Wed, 11 Sep 2024 01:44:25 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20#330=20TimePicker=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20UI/UX=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/svgs/calendar/ic_next_chevron.svg | 2 +- .../assets/svgs/calendar/ic_prev_chevron.svg | 2 +- .../common/dateTimePicker/Calendar.tsx | 5 +- .../common/dateTimePicker/TimePicker.tsx | 84 ++++++++++++++++--- .../common/dateTimePicker/time.const.ts | 2 +- .../component/common/radioButton/Radio.tsx | 9 +- 6 files changed, 84 insertions(+), 20 deletions(-) diff --git a/apps/web/src/assets/svgs/calendar/ic_next_chevron.svg b/apps/web/src/assets/svgs/calendar/ic_next_chevron.svg index 64f89a86..f3a22033 100644 --- a/apps/web/src/assets/svgs/calendar/ic_next_chevron.svg +++ b/apps/web/src/assets/svgs/calendar/ic_next_chevron.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/web/src/assets/svgs/calendar/ic_prev_chevron.svg b/apps/web/src/assets/svgs/calendar/ic_prev_chevron.svg index 4f8b4ee7..c26bcb62 100644 --- a/apps/web/src/assets/svgs/calendar/ic_prev_chevron.svg +++ b/apps/web/src/assets/svgs/calendar/ic_prev_chevron.svg @@ -1,3 +1,3 @@ - + diff --git a/apps/web/src/component/common/dateTimePicker/Calendar.tsx b/apps/web/src/component/common/dateTimePicker/Calendar.tsx index 657a7c09..13186cfc 100644 --- a/apps/web/src/component/common/dateTimePicker/Calendar.tsx +++ b/apps/web/src/component/common/dateTimePicker/Calendar.tsx @@ -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; @@ -10,8 +11,8 @@ export function Calendar({ ...props }: CalendarProps) { new Intl.DateTimeFormat("en", { day: "numeric" }).format(date)} - prevLabel={} - nextLabel={} + prevLabel={} + nextLabel={} {...props} /> ); diff --git a/apps/web/src/component/common/dateTimePicker/TimePicker.tsx b/apps/web/src/component/common/dateTimePicker/TimePicker.tsx index f004632e..82f40a45 100644 --- a/apps/web/src/component/common/dateTimePicker/TimePicker.tsx +++ b/apps/web/src/component/common/dateTimePicker/TimePicker.tsx @@ -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"; @@ -12,33 +12,93 @@ type TimePickerProps = { }; export const TimePicker = forwardRef(function ({ radioControl }, ref) { const { curTab, tabs, selectTab } = useTabs(["오전", "오후"] as const); + const radioButtonsContainerRef = useRef(null); + + const pmFirstItemRef = useRef(null); + const amFirstItemRef = useRef(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 (
- - - {curTab === "오전" && - TIME_24.slice(0, 12).map((time, index) => { + { + 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); + }} + /> +
+ + {TIME_24.slice(0, 13).map((time, index) => { return ( - - {time} + + {time} ); })} - {curTab === "오후" && - TIME_24.slice(12).map((time, index) => { + {TIME_24.slice(13).map((time, index) => { return ( - - {`${Number(time.split(":")[0]) - 12}:00`} + + {time} ); })} - + +
); }); diff --git a/apps/web/src/component/common/dateTimePicker/time.const.ts b/apps/web/src/component/common/dateTimePicker/time.const.ts index 7a4c25a1..fe92cc00 100644 --- a/apps/web/src/component/common/dateTimePicker/time.const.ts +++ b/apps/web/src/component/common/dateTimePicker/time.const.ts @@ -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`); diff --git a/apps/web/src/component/common/radioButton/Radio.tsx b/apps/web/src/component/common/radioButton/Radio.tsx index bd22d8d8..3a246918 100644 --- a/apps/web/src/component/common/radioButton/Radio.tsx +++ b/apps/web/src/component/common/radioButton/Radio.tsx @@ -1,5 +1,5 @@ import { css } from "@emotion/react"; -import { useContext } from "react"; +import { forwardRef, useContext } from "react"; import { RadioContext } from "./RadioButtonGroup"; @@ -11,10 +11,11 @@ type RadioProps = { children: React.ReactNode; } & Omit, "checked">; -export function Radio({ value, children, ...props }: RadioProps) { +export const Radio = forwardRef(function ({ value, children, ...props }, ref) { const radioContext = useContext(RadioContext); return ( ); -} +}); + +Radio.displayName = "Radio"; From 0218aa7424cce7245a3b583595e5f1ebf808ec46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Wed, 11 Sep 2024 01:44:43 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20#330=20=EB=A7=88=EA=B0=90=EC=9D=BC?= =?UTF-8?q?=20=EC=98=B5=EC=85=94=EB=84=90=20=ED=83=80=EC=9E=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/lib/provider/mix-pannel-provider/event.type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/lib/provider/mix-pannel-provider/event.type.ts b/apps/web/src/lib/provider/mix-pannel-provider/event.type.ts index 66a0fdc0..712c23c4 100644 --- a/apps/web/src/lib/provider/mix-pannel-provider/event.type.ts +++ b/apps/web/src/lib/provider/mix-pannel-provider/event.type.ts @@ -18,7 +18,7 @@ type EVENTS_TO_PROPERTIES = { templateId: number; spaceId: number; title: string; - deadline: string; + deadline?: string; }; TEMPLATE_RECOMMEND: Omit; From d0752e237da4bec84c78da7c00b6cfb861a0e7e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=84=86=E1=85=B5=E1=86=AB=E1=84=92?= =?UTF-8?q?=E1=85=B4?= Date: Wed, 11 Sep 2024 01:51:38 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20#330=20=EB=9D=BC=EB=94=94=EC=98=A4?= =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20=EB=94=94=EC=9E=90=EC=9D=B8=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/common/radioButton/Radio.tsx | 19 ++++++++++++++----- .../retrospectCreate/steps/DueDate.tsx | 8 ++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/apps/web/src/component/common/radioButton/Radio.tsx b/apps/web/src/component/common/radioButton/Radio.tsx index 3a246918..fc42522e 100644 --- a/apps/web/src/component/common/radioButton/Radio.tsx +++ b/apps/web/src/component/common/radioButton/Radio.tsx @@ -4,15 +4,24 @@ import { forwardRef, useContext } from "react"; import { RadioContext } from "./RadioButtonGroup"; import { Typography } from "@/component/common/typography"; -import { DESIGN_SYSTEM_COLOR } from "@/style/variable"; +import { DESIGN_TOKEN_COLOR } from "@/style/designTokens"; type RadioProps = { value: string; children: React.ReactNode; + rounded?: "sm" | "lg"; } & Omit, "checked">; -export const Radio = forwardRef(function ({ value, children, ...props }, ref) { +export const Radio = forwardRef(function ({ value, rounded = "sm", children, ...props }, ref) { const radioContext = useContext(RadioContext); + + const STYLE_MAP = { + borderRadius: { + sm: "0.6rem", + lg: "0.8rem", + }, + } as const; + return (