diff --git a/frontend/src/components/AttendeeTooltip/AttendeeTooltip.stories.tsx b/frontend/src/components/AttendeeTooltip/AttendeeTooltip.stories.tsx index c9c1337c9..3a94b902d 100644 --- a/frontend/src/components/AttendeeTooltip/AttendeeTooltip.stories.tsx +++ b/frontend/src/components/AttendeeTooltip/AttendeeTooltip.stories.tsx @@ -50,8 +50,6 @@ export const Playground: Story = { const s_td = css` position: relative; - overflow: hidden; - width: 10rem; max-width: 10rem; height: 4rem; diff --git a/frontend/src/components/AttendeeTooltip/AttendeeTooltip.styles.ts b/frontend/src/components/AttendeeTooltip/AttendeeTooltip.styles.ts index ac60d9f4d..de1a4d6c1 100644 --- a/frontend/src/components/AttendeeTooltip/AttendeeTooltip.styles.ts +++ b/frontend/src/components/AttendeeTooltip/AttendeeTooltip.styles.ts @@ -2,11 +2,6 @@ import { css } from '@emotion/react'; import theme from '@styles/theme'; -export const s_tooltipTrigger = css` - width: 100%; - height: 100%; -`; - export const s_attendeeTooltipContainer = css` display: flex; flex-direction: column; diff --git a/frontend/src/components/AttendeeTooltip/index.tsx b/frontend/src/components/AttendeeTooltip/index.tsx index 48a84e1da..89bf053bc 100644 --- a/frontend/src/components/AttendeeTooltip/index.tsx +++ b/frontend/src/components/AttendeeTooltip/index.tsx @@ -1,4 +1,5 @@ import { css } from '@emotion/react'; +import type { PropsWithChildren } from 'react'; import type { TooltipPosition } from 'types/tooltip'; import Tooltip from '@components/_common/Tooltip'; @@ -8,7 +9,6 @@ import { s_attendeeTooltipContainer, s_attendeesContainer, s_tooltipTitle, - s_tooltipTrigger, } from './AttendeeTooltip.styles'; interface AttendeeTooltipProps { @@ -16,7 +16,11 @@ interface AttendeeTooltipProps { position: TooltipPosition; } -export default function AttendeeTooltip({ attendeeNames, position }: AttendeeTooltipProps) { +export default function AttendeeTooltip({ + attendeeNames, + position, + children, +}: PropsWithChildren) { return ( } visibleStyles={css` - border: 0.3rem dashed #71717a; + outline: 3px dashed #71717a; + outline-offset: -3px; `} > -
+ {children} ); } diff --git a/frontend/src/components/MeetingConfirmCalendar/SingleDate/SingleDateViewer.tsx b/frontend/src/components/MeetingConfirmCalendar/SingleDate/SingleDateViewer.tsx index 859c40c84..9ffaad58f 100644 --- a/frontend/src/components/MeetingConfirmCalendar/SingleDate/SingleDateViewer.tsx +++ b/frontend/src/components/MeetingConfirmCalendar/SingleDate/SingleDateViewer.tsx @@ -49,11 +49,39 @@ export default function SingleDateViewer({ if (selectAttendee !== '' && availableAttendees) return ; }; - const renderTooltip = () => - selectAttendee === '' && - availableAttendees && ; + const hasAttendeeTooltip = selectAttendee === '' && availableAttendees; - return status === 'current' ? ( + if (status !== 'current') return
; + + return hasAttendeeTooltip ? ( + +
+ {date} + {additionalText()} +
+
+ ) : (
{date} {additionalText()} - {renderTooltip()}
- ) : ( -
); } diff --git a/frontend/src/components/Schedules/SchedulePicker/index.tsx b/frontend/src/components/Schedules/SchedulePicker/index.tsx index ca63419b2..1ed421151 100644 --- a/frontend/src/components/Schedules/SchedulePicker/index.tsx +++ b/frontend/src/components/Schedules/SchedulePicker/index.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from 'react'; +import { useContext, useMemo, useState } from 'react'; import type { MeetingDateTime } from 'types/meeting'; import type { MeetingSingleSchedule, Mode } from 'types/schedule'; @@ -56,12 +56,16 @@ export default function SchedulePicker({ const { handleToggleIsTimePickerUpdate } = useContext(TimePickerUpdateStateContext); - const schedules = generateSingleScheduleTable({ - firstTime, - lastTime, - availableDates, - meetingSingleSchedule, - }); + const schedules = useMemo( + () => + generateSingleScheduleTable({ + firstTime, + lastTime, + availableDates, + meetingSingleSchedule, + }), + [availableDates, firstTime, lastTime, meetingSingleSchedule], + ); const { tableRef, diff --git a/frontend/src/components/Schedules/ScheduleViewer/AllSchedules.tsx b/frontend/src/components/Schedules/ScheduleViewer/AllSchedules.tsx index 5773cf06b..776b337f3 100644 --- a/frontend/src/components/Schedules/ScheduleViewer/AllSchedules.tsx +++ b/frontend/src/components/Schedules/ScheduleViewer/AllSchedules.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import type { MeetingDateTime } from 'types/meeting'; import type { MeetingAllSchedules } from 'types/schedule'; diff --git a/frontend/src/components/Schedules/Schedules.styles.ts b/frontend/src/components/Schedules/Schedules.styles.ts index 979eb7d5d..790b7810a 100644 --- a/frontend/src/components/Schedules/Schedules.styles.ts +++ b/frontend/src/components/Schedules/Schedules.styles.ts @@ -110,6 +110,7 @@ export const s_baseTimeCell = (isHalfHour: boolean, isLastRow: boolean) => css` export const s_bottomFixedButtonContainer = css` position: sticky; /* 절대 위치로 부모 컨테이너 내에서 배치 */ + z-index: 1; /* 툴팁이 푸터보다 위에 위치하는 문제를 해결하기 위해서 z-index 추가(@해리) */ bottom: 0; left: 0; diff --git a/frontend/src/components/_common/Tooltip/Tooltip.stories.tsx b/frontend/src/components/_common/Tooltip/Tooltip.stories.tsx index 4c5e91957..ae1d1deab 100644 --- a/frontend/src/components/_common/Tooltip/Tooltip.stories.tsx +++ b/frontend/src/components/_common/Tooltip/Tooltip.stories.tsx @@ -1,4 +1,6 @@ +import { css } from '@emotion/react'; import type { Meta, StoryObj } from '@storybook/react'; +import { Fragment } from 'react'; import Tooltip from '.'; @@ -51,15 +53,36 @@ type Story = StoryObj; export const Playground: Story = { args: { - content:
안녕하세요 툴팁입니다
, - children: , + content: ( +
+ 안녕하세요 툴팁입니다 +
+ ), + children: ( + + ), position: 'top', }, render: (args) => { return ( - - {args.children} - + + + {args.children} + + ); }, }; diff --git a/frontend/src/components/_common/Tooltip/Tooltip.styles.ts b/frontend/src/components/_common/Tooltip/Tooltip.styles.ts index 14ff0952f..e059e3df6 100644 --- a/frontend/src/components/_common/Tooltip/Tooltip.styles.ts +++ b/frontend/src/components/_common/Tooltip/Tooltip.styles.ts @@ -3,89 +3,150 @@ import { css } from '@emotion/react'; import type { TooltipPosition } from 'types/tooltip'; export const tooltipContainer = css` - position: absolute; + position: relative; width: 100%; height: 100%; `; export const tooltipContent = css` position: absolute; + z-index: 1; `; -export const getTooltipPosition = (position: TooltipPosition, targetRect: DOMRect | undefined) => { - if (!targetRect) return ''; - +export const getTooltipPosition = (position: TooltipPosition) => { const positions = { top: css` - top: ${targetRect.top + window.scrollY}px; - left: ${targetRect.left + targetRect.width / 2 + window.scrollX}px; - transform: translate(-50%, -100%); + bottom: 100%; + left: 50%; + transform: translateX(-50%); `, bottom: css` - top: ${targetRect.bottom + window.scrollY}px; - left: ${targetRect.left + targetRect.width / 2 + window.scrollX}px; - transform: translate(-50%, 0); + top: 100%; + left: 50%; + transform: translateX(-50%); `, right: css` - top: ${targetRect.top + targetRect.height / 2 + window.scrollY}px; - left: ${targetRect.right + window.scrollX}px; - transform: translate(0, -50%); + top: 50%; + left: 100%; + transform: translateY(-50%); `, left: css` - top: ${targetRect.top + targetRect.height / 2 + window.scrollY}px; - left: ${targetRect.left + window.scrollX}px; - transform: translate(-100%, -50%); + top: 50%; + right: 100%; + transform: translateY(-50%); `, topLeft: css` - top: ${targetRect.top + window.scrollY}px; - left: ${targetRect.left + window.scrollX}px; - transform: translate(0, -100%); + bottom: 100%; + left: 0; `, topRight: css` - top: ${targetRect.top + window.scrollY}px; - left: ${targetRect.right + window.scrollX}px; - transform: translate(-100%, -100%); + right: 0; + bottom: 100%; `, bottomLeft: css` - top: ${targetRect.bottom + window.scrollY}px; - left: ${targetRect.left + window.scrollX}px; - transform: translate(0, 0); + top: 100%; + left: 0; `, bottomRight: css` - top: ${targetRect.bottom + window.scrollY}px; - left: ${targetRect.right + window.scrollX}px; - transform: translateX(0) translateY(0); + top: 100%; + right: 0; `, leftTop: css` - top: ${targetRect.top + window.scrollY}px; - left: ${targetRect.left + window.scrollX}px; - transform: translate(-100%, 0); + top: 0; + right: 100%; `, leftBottom: css` - top: ${targetRect.bottom + window.scrollY}px; - left: ${targetRect.left + window.scrollX}px; - transform: translate(-100%, -100%); + right: 100%; + bottom: 0; `, rightTop: css` - top: ${targetRect.top + window.scrollY}px; - left: ${targetRect.right + window.scrollX}px; - transform: translate(0, 0); + top: 0; + left: 100%; `, rightBottom: css` - top: ${targetRect.bottom + window.scrollY}px; - left: ${targetRect.right + window.scrollX}px; - transform: translate(0, -100%); + bottom: 0; + left: 100%; `, }; return positions[position] || positions.top; }; +// export const getTooltipPosition = (position: TooltipPosition, targetRect: DOMRect | undefined) => { +// if (!targetRect) return ''; + +// const positions = { +// top: css` +// top: ${targetRect.top + window.scrollY}px; +// left: ${targetRect.left + targetRect.width / 2 + window.scrollX}px; +// transform: translate(-50%, -100%); +// `, +// bottom: css` +// top: ${targetRect.bottom + window.scrollY}px; +// left: ${targetRect.left + targetRect.width / 2 + window.scrollX}px; +// transform: translate(-50%, 0); +// `, +// right: css` +// top: ${targetRect.top + targetRect.height / 2 + window.scrollY}px; +// left: ${targetRect.right + window.scrollX}px; +// transform: translate(0, -50%); +// `, +// left: css` +// top: ${targetRect.top + targetRect.height / 2 + window.scrollY}px; +// left: ${targetRect.left + window.scrollX}px; +// transform: translate(-100%, -50%); +// `, +// topLeft: css` +// top: ${targetRect.top + window.scrollY}px; +// left: ${targetRect.left + window.scrollX}px; +// transform: translate(0, -100%); +// `, +// topRight: css` +// top: ${targetRect.top + window.scrollY}px; +// left: ${targetRect.right + window.scrollX}px; +// transform: translate(-100%, -100%); +// `, +// bottomLeft: css` +// top: ${targetRect.bottom + window.scrollY}px; +// left: ${targetRect.left + window.scrollX}px; +// transform: translate(0, 0); +// `, +// bottomRight: css` +// top: ${targetRect.bottom + window.scrollY}px; +// left: ${targetRect.right + window.scrollX}px; +// transform: translateX(0) translateY(0); +// `, +// leftTop: css` +// top: ${targetRect.top + window.scrollY}px; +// left: ${targetRect.left + window.scrollX}px; +// transform: translate(-100%, 0); +// `, +// leftBottom: css` +// top: ${targetRect.bottom + window.scrollY}px; +// left: ${targetRect.left + window.scrollX}px; +// transform: translate(-100%, -100%); +// `, +// rightTop: css` +// top: ${targetRect.top + window.scrollY}px; +// left: ${targetRect.right + window.scrollX}px; +// transform: translate(0, 0); +// `, +// rightBottom: css` +// top: ${targetRect.bottom + window.scrollY}px; +// left: ${targetRect.right + window.scrollX}px; +// transform: translate(0, -100%); +// `, +// }; + +// return positions[position] || positions.top; +// }; + export const s_tooltipTrigger = ( isVisible: boolean, visibleStyles: SerializedStyles | undefined, ) => css` cursor: pointer; + width: 100%; height: 100%; ${isVisible && visibleStyles} `; diff --git a/frontend/src/components/_common/Tooltip/index.tsx b/frontend/src/components/_common/Tooltip/index.tsx index 89c4d3248..f23e08557 100644 --- a/frontend/src/components/_common/Tooltip/index.tsx +++ b/frontend/src/components/_common/Tooltip/index.tsx @@ -1,7 +1,6 @@ import type { SerializedStyles } from '@emotion/react'; import type { ReactNode } from 'react'; -import { useRef, useState } from 'react'; -import { createPortal } from 'react-dom'; +import { useState } from 'react'; import type { TooltipPosition } from 'types/tooltip'; import { @@ -25,7 +24,6 @@ export default function Tooltip({ visibleStyles, }: TooltipProps) { const [visible, setVisible] = useState(false); - const triggerRef = useRef(null); const showTooltip = () => { setVisible(true); @@ -36,7 +34,7 @@ export default function Tooltip({ }; const positionStyle = { - ...getTooltipPosition(position, triggerRef?.current?.getBoundingClientRect()), + ...getTooltipPosition(position), }; return ( @@ -45,14 +43,13 @@ export default function Tooltip({ css={s_tooltipTrigger(visible, visibleStyles)} onMouseEnter={showTooltip} onMouseLeave={hideTooltip} - ref={triggerRef} > {children}
- {/* cratePortal과 부모 요소 기준 위치를 잡는 것 중 어느 것을 선호하시나요?(@해리) */} - {visible && - createPortal(
{content}
, document.body)} - {/* {visible &&
{content}
} */} + {/* {visible && + createPortal(
{content}
, document.body)} */} + {/* 스크롤을 할 경우 툴팁의 위치가 고정되지 않는 문제가 발생하기 때문에, 부모 요소 기준 상대적으로 툴팁의 위치가 결정되도록 수정 (2024.12.23 @해리) */} + {visible &&
{content}
} ); } diff --git a/frontend/src/hooks/useTimePick/useTimePick.ts b/frontend/src/hooks/useTimePick/useTimePick.ts index 2d6fcbaa0..3345e524a 100644 --- a/frontend/src/hooks/useTimePick/useTimePick.ts +++ b/frontend/src/hooks/useTimePick/useTimePick.ts @@ -91,6 +91,7 @@ export default function useTimePick(initialTableValue: number[][], currentDatePa const handlePointerEnd = useCallback((event: Event) => { startIndex.current = null; + currentIndex.current = null; if (event.cancelable) { event.preventDefault(); } diff --git a/frontend/src/hooks/useTimePick/useTimePick.utils.ts b/frontend/src/hooks/useTimePick/useTimePick.utils.ts index ae5dba4cc..2eeab90c8 100644 --- a/frontend/src/hooks/useTimePick/useTimePick.utils.ts +++ b/frontend/src/hooks/useTimePick/useTimePick.utils.ts @@ -30,9 +30,6 @@ export function getTableCellElement(event: Event): HTMLTableCellElement | null { } export function getTableCellIndex(event: Event) { - let rowIndex: number | null = null; - let colIndex: number | null = null; - const target = getTableCellElement(event); if (!target) { @@ -41,13 +38,15 @@ export function getTableCellIndex(event: Event) { const tr = target.closest('tr'); - if (tr) { - const tds = Array.from(tr.querySelectorAll('td')); - - rowIndex = tr.sectionRowIndex; - colIndex = tds.findIndex((td) => td === target); + if (!tr) { + return null; } + const tds = Array.from(tr.querySelectorAll('td')); + + const rowIndex = tr.sectionRowIndex; + const colIndex = tds.findIndex((td) => td === target); + if (rowIndex === null || colIndex === null || colIndex === -1) { return null; }