Tooltip 컴포넌트 구현

김승규·2023년 6월 19일
1

디자인시스템

목록 보기
4/10

범용적으로 사용할 수있는 Tooltip 개발기를 공유하고자 한다.

작업 결과물

배포된 환경에서 보기

Tooltip

사전 정보

툴팁을 개발할 때 hover 되면 툴팁이 나오도록 설계 했기 때문에 hover 여부를 파악하기 위해 useHover hooks 를 만들어 hover 여부를 판단하는 커스텀훅을 개발하였다.

// hooks/useHover.ts
import { useState, useRef, useEffect } from 'react';

export function useHover<T extends HTMLElement = HTMLElement>() {
  const [isHover, setIsHover] = useState(false);
  const ref = useRef<T | null>(null);

  const handleMouseEnter = () => setIsHover(true);
  const handleMouseLeave = () => setIsHover(false);

  useEffect(() => {
    const node = ref.current;
    if (node) {
      node.addEventListener('mouseenter', handleMouseEnter);
      node.addEventListener('mouseleave', handleMouseLeave);
    }

    return () => {
      if (node) {
        node.removeEventListener('mouseenter', handleMouseEnter);
        node.removeEventListener('mouseleave', handleMouseLeave);
      }
    };
  }, []);

  return { ref, isHover };
}

범용적으로 hover 여부를 처리하기 위해 제네릭을 통해 ref(DOM) 의 타입을 처리하였다.
hover 여부를 판단하기 위해 state 와 mouseenter, mouseleave 이벤트를 통해 dom 진입 및 벗어남을 감지하여 hover 활성화 여부를 파악했다.

툴팁 구현기

// Tooltip.tsx
import { PropsWithChildren } from 'react';

import * as S from './Tooltip.styles.tsx';
import { theme } from '@/styles/theme.ts';
import { useHover } from '@/hooks/usHover.ts';

type TooltipProps = {
  content: string;
  placement?: 'top' | 'right' | 'bottom' | 'left';
  color?: string;
};
export function Tooltip({
  content,
  placement = 'right',
  color = theme.color.gray900,
  children,
}: PropsWithChildren<TooltipProps>) {
  const { ref: tooltipRef, isHover: isVisible } = useHover<HTMLDivElement>();

  return (
    <S.Tooltip ref={tooltipRef}>
      {children}
      {isVisible && (
        <S.Content className={placement} style={{ backgroundColor: color }}>
          {content}
        </S.Content>
      )}
    </S.Tooltip>
  );
}
  • children 으로 컨텐츠를 나오게했고
  • Tooltip props 로 contents 로 받아서 hover 시 tooltip 의 내용을 표현하도록 하였다.
    그 외로도 툴팁의 컨텐츠의 색을 변경하도록 color 를 지정했다.
import styled from '@emotion/styled';

import { theme } from '@/styles/theme.ts';

const { color } = theme;

export const Tooltip = styled.div`
  position: relative;
  display: inline-flex;
  white-space: nowrap;
`;

export const Content = styled.div`
  position: absolute;
  z-index: 100;
  min-height: 40px;
  padding: 10px 12px;
  font-size: 13px;
  line-height: 1.3;
  color: ${color.white};
  border: 1px solid ${color.gray100};
  border-radius: 8px;
  box-shadow: 0 2px 8px rgb(0 0 0 / 10%);
  margin: 0 10px;

  &.top {
    bottom: 100%;
    left: 50%;
    margin-bottom: 5px;
    transform: translateX(-50%);
  }

  &.right {
    top: 50%;
    left: 100%;
    margin-left: 12px;
    transform: translateY(-50%);
  }

  &.bottom {
    top: 100%;
    left: 50%;
    margin-top: 6px;
    transform: translateX(-50%);
  }

  &.left {
    top: 50%;
    right: 100%;
    margin-right: 12px;
    transform: translateY(-50%);
  }
`;
  • Tooltip 스타일을 기준으로 하기 위해 position 을 relative 로 정의하였다.
  • Content 가 실제 Tooltip 의 내용으로 해당 위치(top, right...) 에 따라 스타일을 지정했다.
    absolute 로 정의했기에 bottom, left, right, top 및 translate 속성을 이용하여 위치를 계산하여 지정했다.

개선하고 싶은 내용

  • 툴팁 UI 로 화살표 UI 활성화 여부 hasArrow
  • floating UI 를 활용하여 position 및 interaction 액션 제어하기?

-> 시간 날 때 꼭하자!

reference

0개의 댓글