popover.gif

본 컴포넌트는 단일 파일 구조 내에서 **"플로팅 컨텐츠 노출"**과 **"포커스 관리"**라는 역할을 수행하도록 설계되었다. 외부 클릭 감지 및 Esc 키 바인딩을 통해 웹 접근성(A11y)과 사용자 경험을 최적화하였다.

✅ 컴포넌트 구조

파일명 유형 주요 역할
popover.tsx Client 트리거 요소에 따른 부유(Floating) 컨텐츠의 표시 및 위치를 관리하는 공통 컴포넌트

✅ PopoverProps (공통 속성)

속성명 타입 기본값 설명
isOpen boolean 필수 팝업의 노출 여부를 제어
onClose () => void 필수 닫기 버튼, 배경 클릭, Esc 키 입력 시 호출되는 함수
children ReactNode 필수 팝업 내부에 표시될 컨텐츠
trigger ReactNode - 팝업을 열기 위한 기준 요소를 팝업과 함께 렌더링할 때 사용
align `'left' 'center' 'right'`
center boolean true 화면 정중앙에 배치할지 여부
className string - 팝업 컨텐츠 박스의 스타일을 커스텀
containerClassName string - trigger를 감싸는 컨테이너의 스타일을 커스텀

✅ 사용 예시

1. 상태 제어형 (모달 알림)

가장 일반적인 형태로, 외부 상태(useState)에 따라 화면 중앙에 중요한 알림을 띄운다

"use client";

import Popup from "@/src/shared/ui/popup";
import { useState } from "react";

export default function DeleteConfirm() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>삭제하기</button>

      <Popup isOpen={isOpen} onClose={() => setIsOpen(false)}>
        <div className="text-center">
          <h3 className="text-h3 font-bold mb-2">정말 삭제할까요?</h3>
          <p className="text-body1 text-gray-400 mb-6">삭제된 데이터는 복구할 수 없습니다.</p>
          <div className="flex gap-2">
            <button onClick={() => setIsOpen(false)} className="flex-1 p-3 bg-gray-100 rounded">취소</button>
            <button className="flex-1 p-3 bg-red-500 text-white rounded">삭제</button>
          </div>
        </div>
      </Popup>
    </>
  );
}

2. 트리거 포함형 (정렬 활용)

trigger 속성을 사용하여 팝업과 버튼을 한 곳에서 관리하며, 하단 정렬 등을 커스텀할 수 있다.

<Popup 
  isOpen={showPopup} 
  onClose={() => setShowPopup(false)}
  trigger={<span className="icon">🔔 알림 보기</span>}
  center={false}
  align="right"
  className="w-[400px] mt-auto mb-10" // 하단 우측 배치 예시
>
  <div className="p-4">새로운 알림이 도착했습니다.</div>
</Popup>