diff --git a/src/components/common/Icon.tsx b/src/components/common/Icon.tsx index f9333f48..e9b0cbb9 100644 --- a/src/components/common/Icon.tsx +++ b/src/components/common/Icon.tsx @@ -8,13 +8,13 @@ type IconProps = { size?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge'; color?: 'nomal' | 'strong' | 'heavy' | 'primary' | 'inverse'; onClick?: () => void; - isCusor?: boolean; + isCursor?: boolean; }; -function Icon({ name, size = 'medium', color, onClick, isCusor }: IconProps) { +function Icon({ name, size = 'medium', color, onClick, isCursor }: IconProps) { const SelectedIcon = Icn[name]; return ( - + ); @@ -41,7 +41,7 @@ const getColorMap = (theme: Theme) => ({ const StyledIconWrapper = styled.div<{ size: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge'; color?: 'nomal' | 'strong' | 'heavy' | 'primary' | 'inverse'; - isCusor?: boolean; + isCursor?: boolean; }>` display: flex; align-items: center; @@ -49,7 +49,7 @@ const StyledIconWrapper = styled.div<{ width: ${({ size }) => sizeMap[size]}; height: ${({ size }) => sizeMap[size]}; - ${({ isCusor }) => isCusor && `cursor: pointer;`} + ${({ isCursor }) => isCursor && `cursor: pointer;`} svg { width: 100%; diff --git a/src/components/common/fullCalendar/CalendarHeader.tsx b/src/components/common/fullCalendar/CalendarHeader.tsx new file mode 100644 index 00000000..acac30a4 --- /dev/null +++ b/src/components/common/fullCalendar/CalendarHeader.tsx @@ -0,0 +1,43 @@ +import styled from '@emotion/styled'; + +import IconButton from '../v2/IconButton'; +import MainDate from '../v2/TextBox/MainDate'; + +type Props = { + size: 'small' | 'big'; + date: { year: number; month: number }; +}; + +function CalendarHeader({ size, date }: Props) { + return ( + + + + + + + + ); +} + +export default CalendarHeader; + +const CalendarHeaderContainer = styled.div<{ size: string }>` + position: absolute; + top: 56px; + display: flex; + align-items: flex-start; + justify-content: space-between; + box-sizing: border-box; + width: 100%; + height: auto; + padding: ${({ size }) => (size === 'big' ? '0 2.4rem;' : '0 1.6rem 0 2.4rem;')}; +`; + +const CalendarHeaderWrapper = styled.div` + display: flex; + gap: 194px; + margin-top: 0.2rem; + + color: ${({ theme }) => theme.colorToken.Icon.normal}; +`; diff --git a/src/components/common/fullCalendar/CustomDayCellContent.tsx b/src/components/common/fullCalendar/CustomDayCellContent.tsx new file mode 100644 index 00000000..3c7f46f0 --- /dev/null +++ b/src/components/common/fullCalendar/CustomDayCellContent.tsx @@ -0,0 +1,127 @@ +/* eslint-disable no-nested-ternary */ +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; +import { DayCellContentArg } from '@fullcalendar/core'; +import { useState } from 'react'; + +import { theme } from '@/styles/theme'; + +const TYPE = { + TERITARY: 'Teritary', + SECONDARY: 'Secondary', + PRIMARY: 'Primary', +} as const; + +const STATE = { + DEFAULT: 'Default', + HOVER: 'Hover', + PRESSED: 'Pressed', +} as const; + +type StateType = (typeof STATE)[keyof typeof STATE]; + +interface CustomDayCellContentProps { + arg: DayCellContentArg; + today: string; + selectDate?: string; +} + +// 월간 달력에서 날짜 '일' 제거 및 스타일링 적용 +function CustomDayCellContent({ arg, today, selectDate }: CustomDayCellContentProps) { + const date = new Date(arg.date); + const day = arg.dayNumberText.replace('일', ''); + const isSelectedDate = date.toDateString() === selectDate; + const isToday = date.toDateString() === today; + const [state, setState] = useState(STATE.DEFAULT); + + const type = isToday ? TYPE.PRIMARY : isSelectedDate ? TYPE.SECONDARY : TYPE.TERITARY; + + const handleStateChange = (newState: StateType) => () => { + setState(newState); + }; + + if (arg.view.type === 'dayGridMonth') { + return ( + + {arg.view.type === 'dayGridMonth' ? day : ''} + + ); + } +} + +export default CustomDayCellContent; + +const backgroundStyles = { + Teritary: { + Default: 'transparent', + Hover: theme.colorToken.Primary.strongVariant, + Pressed: theme.colorToken.Primary.heavyVariant, + }, + Secondary: { + Default: 'transparent', + Hover: theme.colorToken.Primary.strongVariant, + Pressed: theme.colorToken.Component.heavy, + }, + Primary: { + Default: theme.colorToken.Primary.normal, + Hover: theme.colorToken.Primary.strong, + Pressed: theme.colorToken.Primary.heavy, + }, +}; + +const textStyles = { + Teritary: theme.colorToken.Text.neutralLight, + Secondary: theme.colorToken.Text.primary, + Primary: theme.colorToken.Text.neutralDark, +}; + +const MonthViewDate = styled.div<{ + type: keyof typeof backgroundStyles; + state: keyof (typeof backgroundStyles)['Teritary']; + isSelectedDate: boolean; + isToday: boolean; +}>` + display: flex; + align-items: center; + justify-content: center; + width: 2.4rem; + height: 2.4rem; + + border-radius: 6px; + + ${({ theme }) => theme.font.label03}; + + ${({ type, state }) => css` + color: ${textStyles[type]}; + + background-color: ${backgroundStyles[type][state]}; + + ${type === TYPE.SECONDARY && underlineStyle} + `} +`; + +const underlineStyle = css` + position: relative; + + &::after { + position: absolute; + bottom: 2px; + left: 50%; + width: 3.2rem; + height: 0.1rem; + + background-color: ${theme.colorToken.Text.primary}; + transform: translateX(-50%); + + content: ''; + } +`; diff --git a/src/components/common/fullCalendar/DayHeaderContent.tsx b/src/components/common/fullCalendar/DayHeaderContent.tsx index 9d0415e0..f70e9922 100644 --- a/src/components/common/fullCalendar/DayHeaderContent.tsx +++ b/src/components/common/fullCalendar/DayHeaderContent.tsx @@ -1,50 +1,55 @@ +/* eslint-disable no-nested-ternary */ import styled from '@emotion/styled'; import { DayHeaderContentArg } from '@fullcalendar/core'; +import SubDate from '../v2/TextBox/SubDate'; + interface DayHeaderContentProps { arg: DayHeaderContentArg; currentView: string; today: string; + selectDate?: string; + size: string; } -function DayHeaderContent({ arg, currentView, today }: DayHeaderContentProps) { +function DayHeaderContent({ arg, currentView, today, selectDate, size }: DayHeaderContentProps) { + let adjustDay = size === 'big' ? 3 : 2; + if (currentView === 'dayGridMonth') { + adjustDay = 0; + } + const adjustedDate = new Date(arg.date); + adjustedDate.setDate(adjustedDate.getDate() - adjustDay); + const isTimeGridDay = currentView === 'timeGridDay'; - const day = new Intl.DateTimeFormat('en-US', { weekday: isTimeGridDay ? 'long' : 'short' }).format(arg.date); - const date = arg.date.getDate(); - const isToday = arg.date.toDateString() === today; + const day = new Intl.DateTimeFormat('ko', { weekday: isTimeGridDay ? 'long' : 'short' }).format(adjustedDate); + const date = adjustedDate.getDate(); + const isSelectedDate = adjustedDate.toDateString() === selectDate; + const isToday = adjustedDate.toDateString() === today; + const isSatday = day === '토'; + const isSunday = day === '일'; return (
- {!isTimeGridDay ? ( - <> - {day} - {currentView !== 'dayGridMonth' && {date}} - + {currentView === 'dayGridMonth' ? ( + + {day} + ) : ( - - {date}일 {day} - + )}
); } -const DayLayout = styled.div` - display: flex; - gap: 1.2rem; - align-items: flex-end; - margin-left: 0.8rem; -`; - -const WeekDay = styled.div<{ isToday: boolean }>` - ${({ theme }) => theme.fontTheme.CAPTION_02}; - color: ${({ isToday, theme }) => (isToday ? theme.palette.Blue.Blue11 : theme.palette.Grey.Grey6)}; +const WeekDay = styled.div<{ isSatday: boolean; isSunday: boolean }>` + ${({ theme }) => theme.font.label04}; + color: ${({ isSatday, isSunday, theme }) => + isSatday ? theme.color.Blue.Blue7 : isSunday ? theme.color.Orange.Orange5 : theme.color.Grey.Grey5}; text-transform: uppercase; `; -const WeekDate = styled.div<{ isToday: boolean }>` - ${({ theme }) => theme.fontTheme.HEADLINE_01}; - color: ${({ isToday, theme }) => (isToday ? theme.palette.Primary : theme.palette.Grey.Black)}; -`; - export default DayHeaderContent; diff --git a/src/components/common/fullCalendar/FullCalendarBox.tsx b/src/components/common/fullCalendar/FullCalendarBox.tsx index 0bc374ec..1a18c013 100644 --- a/src/components/common/fullCalendar/FullCalendarBox.tsx +++ b/src/components/common/fullCalendar/FullCalendarBox.tsx @@ -1,4 +1,3 @@ -import styled from '@emotion/styled'; import { ViewMountArg, DatesSetArg, EventClickArg, EventDropArg } from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import interactionPlugin from '@fullcalendar/interaction'; @@ -9,16 +8,17 @@ import { useState, useRef, useEffect } from 'react'; import ModalDeleteDetail from '../modal/ModalDeleteDetail'; +import CalendarHeader from './CalendarHeader'; +import CustomDayCellContent from './CustomDayCellContent'; import processEvents from './processEvents'; import useDeleteTimeBlock from '@/apis/timeBlocks/deleteTimeBlock/query'; import useGetTimeBlock from '@/apis/timeBlocks/getTimeBlock/query'; import usePostTimeBlock from '@/apis/timeBlocks/postTimeBlock/query'; import useUpdateTimeBlock from '@/apis/timeBlocks/updateTimeBlock/query'; -import RefreshBtn from '@/components/common/button/RefreshBtn'; import DayHeaderContent from '@/components/common/fullCalendar/DayHeaderContent'; import FullCalendarLayout from '@/components/common/fullCalendar/FullCalendarStyle'; -import { customDayCellContent, customSlotLabelContent } from '@/components/common/fullCalendar/fullCalendarUtils'; +import customSlotLabelContent from '@/components/common/fullCalendar/fullCalendarUtils'; import MODAL from '@/constants/modalLocation'; import { TaskType } from '@/types/tasks/taskType'; @@ -29,8 +29,8 @@ interface FullCalendarBoxProps { } function FullCalendarBox({ size, selectDate, selectedTarget }: FullCalendarBoxProps) { - const today = new Date().toDateString(); - const todayDate = new Date().toISOString().split('T')[0]; + const today = new Date(); + const todayDate = today.toISOString().split('T')[0]; const [currentView, setCurrentView] = useState('timeGridWeek'); const [range, setRange] = useState(7); const [startDate, setStartDate] = useState(todayDate); @@ -39,6 +39,7 @@ function FullCalendarBox({ size, selectDate, selectedTarget }: FullCalendarBoxPr const [left, setLeft] = useState(0); const [modalTaskId, setModalTaskId] = useState(null); const [modalTimeBlockId, setModalTimeBlockId] = useState(null); + const [date, setDate] = useState({ year: today.getFullYear(), month: today.getMonth() + 1 }); const calendarRef = useRef(null); @@ -57,18 +58,31 @@ function FullCalendarBox({ size, selectDate, selectedTarget }: FullCalendarBoxPr const handleViewChange = (view: ViewMountArg) => { setCurrentView(view.view.type); - updateRange(view.view.type); + // updateRange(view.view.type); }; const handleDatesSet = (dateInfo: DatesSetArg) => { const currentViewType = dateInfo.view.type; const newStartDate = new Date(dateInfo.start); - newStartDate.setDate(newStartDate.getDate() + 1); + const endDate = new Date(dateInfo.end); + const centerDate = new Date((newStartDate.getTime() + endDate.getTime()) / 2); const formattedStartDate = newStartDate.toISOString().split('T')[0]; setCurrentView(dateInfo.view.type); setStartDate(formattedStartDate); updateRange(currentViewType); + setDate({ + year: centerDate.getFullYear(), + month: centerDate.getMonth() + 1, + }); + + // 월간뷰 스크롤 제거 위해 'month-view' 클래스명 추가 + const calendarContainer = document.querySelector('.fc'); + if (dateInfo.view.type === 'dayGridMonth') { + calendarContainer?.classList.add('month-view'); + } else { + calendarContainer?.classList.remove('month-view'); + } }; const updateRange = (viewType: string) => { @@ -76,12 +90,9 @@ function FullCalendarBox({ size, selectDate, selectedTarget }: FullCalendarBoxPr case 'dayGridMonth': setRange(30); break; - case 'timeGridWeek': + case 'timeGridWeekCustom': setRange(7); break; - case 'timeGridDay': - setRange(1); - break; default: setRange(7); } @@ -186,45 +197,38 @@ function FullCalendarBox({ size, selectDate, selectedTarget }: FullCalendarBoxPr }; return ( - - - - + + } + dayHeaderContent={(arg) => ( + + )} viewDidMount={handleViewChange} datesSet={handleDatesSet} - dayCellContent={customDayCellContent} + dayCellContent={(arg) => ( + + )} eventTimeFormat={{ hour: 'numeric', minute: '2-digit', @@ -255,14 +269,4 @@ function FullCalendarBox({ size, selectDate, selectedTarget }: FullCalendarBoxPr ); } -const CustomButtonContainer = styled.div` - display: flex; - align-items: center; - justify-content: flex-end; - box-sizing: border-box; - width: 100%; - margin-bottom: -2.6rem; - padding-right: 1rem; -`; - export default FullCalendarBox; diff --git a/src/components/common/fullCalendar/FullCalendarStyle.ts b/src/components/common/fullCalendar/FullCalendarStyle.ts index 7c3cd9ac..ad8fab1c 100644 --- a/src/components/common/fullCalendar/FullCalendarStyle.ts +++ b/src/components/common/fullCalendar/FullCalendarStyle.ts @@ -1,17 +1,216 @@ import styled from '@emotion/styled'; -const FullCalendarLayout = styled.div<{ size: string }>` - width: ${({ size }) => (size === 'big' ? '91rem' : '58rem')}; - height: 70rem; +const FullCalendarLayout = styled.div<{ size: string; currentView: string }>` + position: relative; + + box-sizing: border-box; + width: ${({ size }) => (size === 'big' ? '132rem' : '88.8rem')}; + height: 106.4rem; + padding: 0 10px 8px; + overflow: hidden; + + background-color: ${({ theme }) => theme.color.Grey.White}; + border: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralNormal}; + border-radius: 20px; .fc .fc-toolbar.fc-header-toolbar { - margin-bottom: 1.8rem; + display: flex; + align-items: flex-start; + margin: 2.4rem 0 0; + } + + .fc-toolbar { + position: relative; + } + + /* Custom button styles */ + .fc-toolbar-chunk .fc-button { + display: flex; + align-items: center; + justify-content: center; + ${({ theme }) => theme.font.label04} + height: 3.2rem; + padding: 0 1.6rem; + + background: ${({ theme }) => theme.color.Grey.White}; + border: none; + } + + .fc-toolbar-chunk .fc-button:hover, + .fc-toolbar-chunk .fc-button:active { + background: none; + } + + /** .fc-button-group: 주/월 토글 */ + .fc .fc-button-group { + position: absolute; + top: 0; + left: 50%; + overflow: hidden; + + background: ${({ theme }) => theme.color.Grey.White}; + transform: translateX(-50%); + border: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralNormal}; + border-radius: 8px; + } + + .fc-button-group > button { + width: 8rem; + + border-radius: 0; + } + + /** .fc-button-primary: .fc-button-group 중 선택된 버튼 */ + .fc .fc-button-primary:focus { + box-shadow: none; + } + + .fc .fc-button-primary:not(:disabled).fc-button-active { + background: ${({ theme }) => theme.colorToken.Neutral.heavy}; + border: none; + border-radius: 0 7px 7px 0; + } + + .fc-button-group > button:last-of-type.fc-button-primary:not(:disabled).fc-button-active { + border-radius: 7px 0 0 7px; + } + + /* Override the button group border-radius styles */ + .fc-direction-ltr .fc-button-group > .fc-button { + display: flex; + align-items: center; + + color: ${({ theme }) => theme.colorToken.Icon.strong}; + } + + .fc-button-active:focus { + box-shadow: none; + } + + .fc-toolbar-chunk { + display: flex; + gap: 8px; + align-items: center; + margin-right: 7.2rem; } - .fc .fc-timegrid-slot-label-cushion { - padding: 0 1.2rem 0 0; + /* 오늘 버튼 */ + .fc-toolbar-chunk .fc-today-button { + display: flex; + gap: 8px; + align-items: center; + justify-content: center; + margin: 3.4rem 0 0; + + color: ${({ theme }) => theme.color.Grey.Grey5}; + + background-color: ${({ theme }) => theme.colorToken.Neutral.normal}; + opacity: 1; + border: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralNormal}; + ${({ theme }) => theme.font.label04} + border-radius: 8px; + } + + .fc-toolbar-chunk .fc-today-button:active { + color: ${({ theme }) => theme.color.Grey.Grey5}; + + background-color: ${({ theme }) => theme.colorToken.Neutral.heavy}; + border: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; + } + + /* 좌우 버튼 스타일 */ + .fc-toolbar-chunk .fc-prev-button, + .fc-toolbar-chunk .fc-next-button { + display: flex; + align-items: center; + justify-content: center; + width: 3.2rem; + margin: 3.4rem 0 0; + + color: ${({ theme }) => theme.color.Grey.Grey5}; + + background-color: ${({ theme }) => theme.color.Grey.White}; + border: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralNormal}; + border-radius: 8px; + } + + .fc-toolbar-chunk .fc-prev-button:active, + .fc-toolbar-chunk .fc-next-button:active { + color: ${({ theme }) => theme.color.Grey.Grey5}; + + background-color: ${({ theme }) => theme.colorToken.Neutral.heavy}; + border: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; } + /* ---- 여기까지 toolbar (캘린더 헤더) */ + + .fc-daygrid-body { + width: 100% !important; + } + + .fc-event-allday { + height: 2rem; + } + + .fc .fc-col-header-cell { + height: 2.4rem; + padding: 2.4rem 0.8rem 0.5rem; + + border-right: none; + border-left: none; + } + + /* 종일 - 타임그리드 셀 크기 고정 */ + .fc-scrollgrid-sync-table > colgroup > col { + width: 3rem !important; + } + + .fc-timegrid-axis .fc-scrollgrid-shrink { + width: fit-content; + } + + .fc-timegrid-body .fc-timegrid-slots > colgroup > col { + width: 3rem; + } + + /* 전체 캘린더(주간) */ + .fc-scrollgrid.fc-scrollgrid-liquid { + /* padding: 0 8px 0 0; */ + } + + /* fc-daygrid-day-events: 종일 행(개별) */ + .fc-daygrid-day-frame .fc-scrollgrid-sync-inner, + .fc-daygrid-day-events { + width: ${({ size }) => (size === 'big' ? '17.6rem' : '16.8rem')}; + height: 4.4rem; + } + + .fc-timegrid-slot .fc-timegrid-slot-label .fc-scrollgrid-shrink { + width: 4rem; + } + + .fc-timegrid-allday { + display: flex; + align-items: center; + height: 4.4rem; + overflow: hidden; + } + + .fc-timegrid-allday-bg { + height: 4.4rem; + + border-bottom: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralNormal}; + } + + /* 타임그리드 테두리 */ + .fc .fc-scrollgrid-section-body table, + .fc .fc-scrollgrid-section-footer table { + border-right: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; + border-bottom-style: hidden; + } + + /* 주간캘린더 */ + /* 이벤트 박스 */ .fc-event-main { display: inline-block; @@ -45,6 +244,12 @@ const FullCalendarLayout = styled.div<{ size: string }>` background-color: ${({ theme }) => theme.palette.Grey.Grey2}; } + /* 종일 텍스트 */ + .fc-scrollgrid-shrink-frame .fc-scrollgrid-sync-inner { + ${({ theme }) => theme.font.caption02} + color: ${({ theme }) => theme.colorToken.Text.disable}; + } + /* 종일 이벤트 테두리 */ .fc .fc-daygrid-day-frame .fc-event-main { display: flex; @@ -110,14 +315,32 @@ const FullCalendarLayout = styled.div<{ size: string }>` ${({ theme }) => theme.fontTheme.CAPTION_01}; } - /* 타임 그리드 30분당 일정 */ + .fc .fc-col-header-cell-cushion { + padding: 6px 4px; + } + + /** .fc-timegrid-slot-label: 시간 라벨 */ + + /* 타임 그리드 15분당 일정 */ .fc .fc-timegrid-slot-label { - width: 5.7rem; - height: 2.4rem; + display: flex; + align-items: center; + justify-content: flex-end; + box-sizing: border-box; + width: 4rem; - color: ${(color) => color.theme.palette.Grey.Grey6}; + color: ${(color) => color.theme.palette.Grey.Grey5}; + text-transform: lowercase; + + ${({ theme }) => theme.font.caption01} + border-bottom: none; + } + .fc-theme-standard td, + .fc-theme-standard th { + border-right: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; border-bottom: none; + border-left: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; } /* 요일 행 첫번째 border 없애기 */ @@ -126,75 +349,69 @@ const FullCalendarLayout = styled.div<{ size: string }>` border: none; } - /* 타임 그리드 종일 일정 */ - .fc-scrollgrid-shrink { - max-height: 2.4rem; - } - /* 타임 그리드 종일 마진 없애기 */ .fc .fc-daygrid-body-natural .fc-daygrid-day-events { margin: 0; - - border-bottom: 1px solid ${({ theme }) => theme.palette.Grey.Grey9}; } - /* 30분 줄선 지우기 */ - .fc .fc-timegrid-slot-minor { - border-top-style: none; + /* 월간뷰 border 위아래 짤림 커버용 */ + .month-view .fc-scroller.fc-scroller-liquid-absolute { + border-top: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong} !important; + border-bottom: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong} !important; + border-radius: 12px; } - /* 요일 헤더 높이 조정 */ + /* 월간뷰 스크롤 제거 */ + .month-view .fc-scroller { + overflow: hidden !important; + } - .fc .fc-col-header-cell { - box-sizing: border-box; - height: 5.5rem; - padding: 0.4rem 0.8rem 0.6rem; + .fc-daygrid-body { + padding-right: 1rem; + overflow: hidden; - border-right: none; - border-left: none; - border-radius: 8px 8px 0 0; - } + border: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; + border-radius: 12px; - /* 오늘 배경색 없애기 */ - .fc .fc-day-today { - background: none; + ${({ currentView }) => + currentView === 'timeGridWeekCustom' && + ` + border-left: none; + border-radius: 0 + `} } - /* 주말 색 다르게 */ - .fc .fc-day-sun, - .fc .fc-day-sat { - background: ${({ theme }) => theme.palette.Blue.Blue1}; + /* 15분 줄선 테두리 */ + .fc .fc-timegrid-slot-minor { + border: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralNormal}; + border-right: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; + border-left: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; } - .fc .fc-button-primary:not(:disabled).fc-button-active { - background: ${({ theme }) => theme.palette.Primary}; + tr td.fc-timegrid-slot-minor[data-time$=':45:00']:nth-of-type(2) { + border-bottom: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; } - .fc .fc-button-primary:focus { - box-shadow: none; + .fc-scrollgrid .fc-scrollgrid-liquid { + height: 100%; } - /* Custom button styles */ - .fc-toolbar-chunk .fc-button { - width: 4.5rem; - height: 2.6rem; - padding: 0; + /* 요일 헤더 높이 조정 */ - background-color: ${({ theme }) => theme.palette.Blue.Blue3}; - border: none; - border-radius: 8px; + /** .fc-daygrid-day: 각 날짜 별 박스 */ + .fc-daygrid-day { + width: ${({ size }) => (size === 'big' ? '13.2rem' : '12.4rem')}; + height: ${({ currentView }) => (currentView === 'timeGridWeekCustom' ? '0' : '15.2rem')}; } - .fc-toolbar-chunk .fc-button:active { - background-color: ${({ theme }) => theme.palette.Blue.Blue3}; + .fc-daygrid-day-number { + ${({ theme }) => theme.font.label03} + color: ${({ theme }) => theme.colorToken.Text.neutralLight}; } - /* Override the button group border-radius styles */ - .fc-direction-ltr .fc-button-group > .fc-button { - margin-right: 0.4rem; - margin-left: 0; - - border-radius: 8px; + /* 오늘 배경색 없애기 */ + .fc .fc-day-today { + background: none; } /* 스타일링 현재 시간 표시 */ @@ -207,9 +424,7 @@ const FullCalendarLayout = styled.div<{ size: string }>` /* 시간 세로줄 테두리 없애기 */ .fc-timegrid-axis { - color: ${({ theme }) => theme.palette.Grey.Grey6}; - - border: none; + border: 100px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; } .fc-timegrid-event { @@ -227,71 +442,6 @@ const FullCalendarLayout = styled.div<{ size: string }>` inset: 0.1rem; } - /* 좌우 버튼 스타일 */ - .fc-toolbar-chunk .fc-prev-button, - .fc-toolbar-chunk .fc-next-button { - width: 2.6rem; - height: 2.6rem; - padding: 0; - - background-color: ${({ theme }) => theme.palette.Grey.Black}; - } - - .fc-toolbar-chunk .fc-button:hover { - background-color: ${({ theme }) => theme.palette.Blue.Blue8}; - } - - .fc-toolbar-chunk .fc-prev-button:hover, - .fc-toolbar-chunk .fc-next-button:hover { - background-color: ${({ theme }) => theme.palette.Grey.Grey7}; - } - - .fc-direction-ltr .fc-toolbar > * > :not(:first-of-type) { - margin-left: 0.4rem; - } - - .fc-button-active:focus { - box-shadow: none; - } - - .fc-toolbar-chunk { - display: flex; - align-items: center; - } - - /* 오늘 버튼 */ - .fc-toolbar-chunk .fc-today-button { - background-color: ${({ theme }) => theme.palette.Grey.Black}; - opacity: 1; - } - - .fc-toolbar-chunk .fc-today-button:hover { - background-color: ${({ theme }) => theme.palette.Grey.Grey7}; - } - - .fc-toolbar-chunk .fc-today-button:active { - background-color: ${({ theme }) => theme.palette.Grey.Grey8}; - } - - .fc .fc-button-group { - margin-left: 5.4rem; - } - - .fc .fc-custom-button { - background-color: ${({ theme }) => theme.palette.Grey.Black}; - } - - .fc .fc-custom-button:hover { - background-color: ${({ theme }) => theme.palette.Grey.Grey7}; - } - - /* 오늘 버튼 마진 */ - .fc .fc-toolbar-title { - margin-right: 0.75rem; - padding: 0 1rem; - ${({ theme }) => theme.fontTheme.HEADLINE_02}; - } - .fc .fc-daygrid-event-harness { margin: 0; } @@ -352,7 +502,8 @@ const FullCalendarLayout = styled.div<{ size: string }>` } .fc .fc-timegrid-axis-frame { - justify-content: flex-start; + justify-content: center; + width: 4rem; } /* 시간 왼쪽에 붙이기 */ @@ -373,7 +524,7 @@ const FullCalendarLayout = styled.div<{ size: string }>` /* 바깥 테두리 없애기 */ .fc .fc-scrollgrid-liquid { - height: 65.5rem; + height: 100%; overflow: auto; border: none; @@ -406,12 +557,13 @@ const FullCalendarLayout = styled.div<{ size: string }>` background: none; } + /* 일요일 border 조정 */ .fc-dayGridMonth-view .fc-day-sun .fc-daygrid-day-frame { - box-shadow: 0 1px 0 0 ${({ theme }) => theme.palette.Grey.Grey9} inset; + border-bottom: 1px solid ${({ theme }) => theme.colorToken.Outline.neutralStrong}; } - .fc .fc-dayGridMonth-view .fc-scrollgrid-section-body table { - border: 1px solid ${({ theme }) => theme.palette.Grey.Grey9}; + .fc-dayGridMonth-view .fc-daygrid-body tr:last-child .fc-day-sun .fc-daygrid-day-frame { + border-bottom: none; } .fc .fc-timeGridDay-view .fc-col-header-cell-cushion { diff --git a/src/components/common/fullCalendar/fullCalendarUtils.ts b/src/components/common/fullCalendar/fullCalendarUtils.ts index 2f516c04..0109093a 100644 --- a/src/components/common/fullCalendar/fullCalendarUtils.ts +++ b/src/components/common/fullCalendar/fullCalendarUtils.ts @@ -1,24 +1,7 @@ -import { DayCellContentArg, SlotLabelContentArg } from '@fullcalendar/core'; - -// 월간 달력에서 날짜 '일' 제거 -export const customDayCellContent = (info: DayCellContentArg) => { - const number = document.createElement('a'); - number.classList.add('fc-daygrid-day-number'); - number.innerHTML = info.dayNumberText.replace('일', ''); - - if (info.view.type === 'dayGridMonth') { - return { - html: number.outerHTML, - }; - } - - return { - domNodes: [], - }; -}; +import { SlotLabelContentArg } from '@fullcalendar/core'; // 일간, 주간에서 왼쪽 시간형식 '12 AM' 으로 만들기 -export const customSlotLabelContent = (arg: SlotLabelContentArg) => { +const customSlotLabelContent = (arg: SlotLabelContentArg) => { const formattedTime = new Intl.DateTimeFormat('en-US', { hour: 'numeric', hour12: true, @@ -31,3 +14,5 @@ export const customSlotLabelContent = (arg: SlotLabelContentArg) => { html: span.outerHTML, }; }; + +export default customSlotLabelContent; diff --git a/src/components/common/v2/IconButton.tsx b/src/components/common/v2/IconButton.tsx index c5feb590..d448ad97 100644 --- a/src/components/common/v2/IconButton.tsx +++ b/src/components/common/v2/IconButton.tsx @@ -9,7 +9,7 @@ type SizeType = 'small' | 'big'; type IconBtnType = 'solid' | 'normal' | 'outlined'; type IconButtonProps = { type: IconBtnType; - size: SizeType; + size?: SizeType; disabled?: boolean; iconName: keyof typeof Icn; onClick?: () => void; diff --git a/src/components/common/v2/TextBox/MainDate.tsx b/src/components/common/v2/TextBox/MainDate.tsx index aba27482..cbc61227 100644 --- a/src/components/common/v2/TextBox/MainDate.tsx +++ b/src/components/common/v2/TextBox/MainDate.tsx @@ -72,6 +72,7 @@ const MainDateContainer = styled.div` display: flex; gap: 0.8rem; align-items: flex-end; + width: fit-content; background-color: ${theme.color.Grey.White}; diff --git a/src/components/common/v2/popup/ClassificationBox.tsx b/src/components/common/v2/popup/ClassificationBox.tsx index c1ed5b98..180c3518 100644 --- a/src/components/common/v2/popup/ClassificationBox.tsx +++ b/src/components/common/v2/popup/ClassificationBox.tsx @@ -46,9 +46,9 @@ function ClassificationBox({ categoryName, label }: ClassificationBoxProps) { {label} {isClicked ? ( - + ) : ( - + )} {isClicked && ( diff --git a/src/components/common/v2/popup/DeadlineBox.tsx b/src/components/common/v2/popup/DeadlineBox.tsx index fa4f6cff..59c4d1f6 100644 --- a/src/components/common/v2/popup/DeadlineBox.tsx +++ b/src/components/common/v2/popup/DeadlineBox.tsx @@ -61,9 +61,9 @@ function DeadlineBox({ date, startTime, endTime, label }: DeadlineBoxProps) { {label} {isClicked ? ( - + ) : ( - + )} {isClicked && ( diff --git a/src/components/targetArea/TargetControlSection.tsx b/src/components/targetArea/TargetControlSection.tsx index aae52747..253a5900 100644 --- a/src/components/targetArea/TargetControlSection.tsx +++ b/src/components/targetArea/TargetControlSection.tsx @@ -31,7 +31,7 @@ function TargetControlSection({ <> - + {isModalOpen && ( theme.color.Grey.White}; - border: 1px solid ${({ theme }) => theme.palette.Grey.Grey3}; - border-radius: 12px; `;